8000 feat(ngAsDirective): new as directive to publish component controllers into current scope by drpicox · Pull Request #14080 · angular/angular.js · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat(ngAs): new as directive to attach children component controllers…
… to the current controller
  • Loading branch information
drpicox committed Mar 13, 2017
commit ea8672fef804bf80d5f26618f1769fcecaf9bf0d
1 change: 1 addition & 0 deletions angularFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ var angularFiles = {
'src/ng/directive/attrs.js',
'src/ng/directive/form.js',
'src/ng/directive/input.js',
'src/ng/directive/ngAs.js',
'src/ng/directive/ngBind.js',
'src/ng/directive/ngChange.js',
'src/ng/directive/ngClass.js',
Expand Down
2 changes: 2 additions & 0 deletions src/AngularPublic.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
scriptDirective,
selectDirective,
optionDirective,
ngAsDirective,
ngBindDirective,
ngBindHtmlDirective,
ngBindTemplateDirective,
Expand Down Expand Up @@ -179,6 +180,7 @@ function publishExternalAPI(angular) {
script: scriptDirective,
select: selectDirective,
option: optionDirective,
ngAs: ngAsDirective,
ngBind: ngBindDirective,
ngBindHtml: ngBindHtmlDirective,
ngBindTemplate: ngBindTemplateDirective,
Expand Down
148 changes: 148 additions & 0 deletions src/ng/directive/ngAs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
'use strict';

/**
* @ngdoc directive
* @name ngAs
* @restrict A
*
* @description
* The `ngAs` attribute tells Angular to assign element component controller
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the element component controller to a the given property"?

* to a given property.
*
* Using this directive you can use the controller of existing components
* in your template (children components).
*
* If the children component is destroyed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure what our conventions are here, but this line looks pretty short...?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"children" can probably be dropped from this sentence

* a `null` is assigned to the property.
< 8000 details-menu class="dropdown-menu dropdown-menu-sw show-more-popover color-fg-default" style="width:185px" src="" preload > Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably drop the "a"?

*
* Note that this is the reverse of `require:`:
* with `require:` is the children who references the parent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like to reword this a bit. require can also reference the same element, not only parents, and ngAs could also be used either way (such as ng-as="objectOnParent.childController").

* but with `ngAs`is the parent who references the children.
* It is very useful when you want to reuse the same component
* in different situations,
* and they do not need to know which exact parent they have.
*
*
* @element ANY
* @param {expression} ngAs {@link guide/expression Expression} to assign the controller.
*
*
* @example
* ### Use inside the scope
* This example shows how the controller of the component toggle
* is reused in the template through the scope to use its logic.
* <example name="ngAsDirectiveComponentExample" module="ngAsExample">
* <file name="index.html">
* <toggle ng-as="myToggle"></toggle>
* <button ng-click="myToggle.toggle()">Toggle</button>
* <div ng-show="myToggle.isOpen()">You are using a children component to show it.</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a sibling component, not child?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this point of view yes.
Probably I should add a parent component to make it more explicit that it is the parent template who creates the relationships.

* </file>
* <file name="script.js">
* angular.module('ngAsExample', [])
* .component('toggle', {
* controller: function() {
* var opened = false;
* this.isOpen = function() { return opened; };
* this.toggle = function() { opened = !opened; };
* }
* });
* </file>
* <file name="protractor.js" type="protractor">
* it('should publish the toggle into the scope', function() {
* var toggle = element(by.buttonText('Toggle'));
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(false);
* toggle.click();
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(true);
* });
* </file>
* </example>
*
* @example
* ### Parent interacts with child via member
* This example shows how the parent controller can have access
* to children component controllers.
* <example name="ngAsDirectiveComponentCounterExample" module="ngAsVoteExample">
* <file name="index.html">
* <competition></competition>
* </file>
* <file name="script.js">
* angular.module('ngAsVoteExample', [])
* .component('voteTaker', {
* controller: function() {
* this.vote = null;
* this.agree = function() { this.vote = 'agree'; };
* this.disagree = function() { this.vote = 'disagree'; };
* this.clear = function() { this.vote = null; };
* },
* template:
* '<button ng-disabled="$ctrl.vote" ng-click="$ctrl.agree()">Agree</button>' +
* '<button ng-disabled="$ctrl.vote" ng-click="$ctrl.disagree()">Disagree</button>'
* })
* .component('competition', {
* controller: function() {
* this.redVoteTaker = null;
* this.blueVoteTaker = null;
* this.match = function() {
* return this.redVoteTaker.vote === this.blueVoteTaker.vote && this.redVoteTaker.vote;
* };
* this.next = function() {
* this.sentence++;
* this.redVoteTaker.clear();
* this.blueVoteTaker.clear();
* };
* },
* template:
* '<p>Red team: <vote-taker ng-as="$ctrl.redVoteTaker"></vote-taker></p>' +
* '<p>Blue team: <vote-taker ng-as="$ctrl.blueVoteTaker"></vote-taker></p>' +
* '<p ng-show="$ctrl.match()">There is a match!</p>' +
* '<button ng-click="$ctrl.next()">Next</button>'
* });
* </file>
* <file name="protractor.js" type="protractor">
* var agrees = element.all(by.buttonText('Agree'));
* var matchMessage = element(by.css('[ng- 9E88 show]'));
* var next = element(by.buttonText('Next'));
*
* it('should show match message if both agree', function() {
* expect(matchMessage.isDisplayed()).toBeFalsy();
* agrees.click();
* expect(matchMessage.isDisplayed()).toBeTruthy();
* });
*
* it('should hide match message after next is clicked', function() {
* agrees.click();
* next.click();
* expect(matchMessage.isDisplayed()).toBeFalsy();
* });
* </file>
* </example>
*/
var ngAsDirective = ['$parse', function($parse) {
return {
priority: -1,
restrict: 'A',
compile: function(tElement, tAttrs) {
// gets the expected controller name, converts <data-some-thing> into "someThing"
var controllerName = directiveNormalize(nodeName_(tElement));

// get the setter for the as attribute
var getter = $parse(tAttrs.ngAs);
var setter = getter.assign;

return function(scope, element) {
// gets the controller of the current element (see jqLiteController for details)
var controller = element.data('$' + controllerName + 'Controller');
setter(scope, controller);

// when the element is removed, remove it from the scope assignment (nullify it)
element.on('$destroy', function() {
// only remove it if controller has not changed,
// because it can happen that animations (and other procedures) may duplicate elements
if (getter(scope) === controller) {
setter(scope, null);
}
});
};
}
};
}];
Loading
0