-
Notifications
You must be signed in to change notification settings - Fork 27.5k
Feat: ngRef directive to publish controllers, or elements into current scope #16511
Conversation
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
1 similar comment
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
src/ng/directive/ngRef.js
Outdated
// only remove it if value has not changed, | ||
// carefully because animations (and other procedures) may duplicate elements | ||
if (getter(scope) === refValue) { | ||
setter(scope, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra space
src/ng/directive/ngRef.js
Outdated
var setter = getter.assign; | ||
|
||
return function(scope, element, attrs) { | ||
var refValue = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the null
value is necessary
src/ng/directive/ngRef.js
Outdated
|
||
// get the expression for value binding | ||
var getter = $parse(tAttrs.ngRef); | ||
var setter = getter.assign; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we throw a nonassign
if the expression can not be assigned to?
src/ng/directive/ngRef.js
Outdated
* to the given property in the current scope. If no controller exists, the jqlite-wrapped DOM | ||
* element will be added to the scope. | ||
* | ||
* If the element with ngRef is destroyed `null` is assigned to the property. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ngRef
--> `ngRef`
src/ng/directive/ngRef.js
Outdated
* </div> | ||
* </file> | ||
* <file name="index.js"> | ||
* angular.module('myApp', []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this might be better for a real app, having multiple, tiny files in examples is harder to follow. I would put everything in one JS file (and avoid unnecessary redirection, e.g.:
angular.
module('myApp', []).
component('myToggle', {
controller: function MyToggleController(...) { ... }
});
* var toggle = element(by.buttonText('Toggle')); | ||
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(false); | ||
* toggle.click(); | ||
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, I didn't know about the evaluate
method. Nice 😃
src/ng/directive/ngRef.js
Outdated
* | ||
* @example | ||
* ### ngRef inside scopes | ||
* This example shows how new scopes limits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Limits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this sentence at all :-)
src/ng/directive/ngRef.js
Outdated
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div> | ||
* </li> | ||
* </ul> | ||
* <div>ngRepeat.isOpen(): {{ngRepeatToggle.isOpen() | json}}</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ngRepeat
--> ngRepeatToggle
src/ng/directive/ngRef.js
Outdated
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div> | ||
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div> | ||
* </div> | ||
* <div>ngIf.isOpen(): {{ngIf.isOpen() | json}}</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ngIf
--> ngIfToggle
src/ng/directive/ngRef.js
Outdated
* </example> | ||
* | ||
*/ | ||
var ngRefDirective = ['$parse',function($parse) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space after ,
.
* }); | ||
* }); | ||
* </file> | ||
* </example> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the examples, it looks like we suggest putting these stuff on the scope (I hope we don't).
It would be better if showcased how to assign the values into controllers, instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I didn't write the examples, but can fix them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that in the simple case where you just want to use the value inside the HTML only it makes sense to assign the ref to the scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed the 2nd example to use $ctrl, but not the first. I think we can keep it like this - the whole docs are riddled with "old style" examples
src/ng/directive/ngRef.js
Outdated
* @param {string} ngRef property name - A valid AngularJS expression identifier to which the | ||
* controller or jqlite-wrapped DOM element will be bound. | ||
* @param {string=} ngRefElement void - If specified, ngRef will always bind the jqlite-wrapped | ||
* DOM-element even if a controller is available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the void -
bit mean in the description here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is supposed to signify that the attribute takes no value. :)
src/ng/directive/ngRef.js
Outdated
return function(scope, element, attrs) { | ||
var refValue = null; | ||
|
||
if ('ngRefElement' in attrs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, would this preclude the use of both ngRef
and ngRefElement
used in conjunction to get both the controller and element?
Never mind, it's late. Thought this was a separate directive, then realized it was an option.
The API design has changed after discussing it with @petebacondarwin - there is now a ngRefRead attribute that can be used to assign a specific directive's controller or the element. The default behavior is still that the component /element directive's controller, or if there's no component / directive, the element is assigned. |
@Narretz is this now ready for a full review? |
@petebacondarwin not quite. I'll try to finish it tonight. |
1e610f1
to
935bb71
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few minor comments
|
||
``` | ||
|
||
To resolve this error, use a path expression that are assignable: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is assignable
test/ng/directive/ngRefSpec.js
Outdated
expect($rootScope.mySpan[0].textContent).toBe('my text'); | ||
})); | ||
|
||
it('should nullify the dom element value if it is destroyed', inject(function($compile, $rootScope) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test description accurate? It implies, to me at least, that the value of the DOM element is being set to null
.
I think we are actually setting the value of the expression to null, right?
|
||
expect($rootScope.myDirective).toBe(myDirectiveController); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth having a test that shows the element directive / component is chosen over any additional attribute directives if no ngRefRead
attribute is available?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will add
}); | ||
|
||
inject(function($compile, $rootScope) { | ||
var template = '<my-component ng-ref="myComponent">{{myComponent.text}}</my-component>'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woah! This makes you think :-)
src/ng/directive/ngRef.js
Outdated
* @restrict A | ||
* | ||
* @description | ||
* The `ngRef` attribute tells AngularJS to assign the controller of a component (or an element-directive) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also assign attribute directives if we use ngRefRead
so this sentence is not entirely accurate.
* }); | ||
* }); | ||
* </file> | ||
* </example> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that in the simple case where you just want to use the value inside the HTML only it makes sense to assign the ref to the scope.
I've updated the PR with your suggestions @petebacondarwin |
@@ -313,6 +313,7 @@ beforeEach(function() { | |||
|
|||
function generateCompare(isNot) { | |||
return function(actual, namespace, code, content) { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... 🤔
src/ng/directive/ngRef.js
Outdated
|
||
var ngRefDirective = ['$parse', function($parse) { | ||
return { | ||
priority: -1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the -1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed for transclusion compatibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it because you are doing work in the compile function?
(Does this work have to be done there in the first place?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like it's because of the compile fn. Without the priority of -1, the link function receives the transclusion comment instead of the actual element if the element has "element transclusion" on it. I haven't looked into why it happens like this, though.
src/ng/directive/ngRef.js
Outdated
var refValue; | ||
var controller; | ||
|
||
if ('ngRefRead' in attrs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use .hasOwnProperty()
instead (to avoid traversing the prototype chain)?
src/ng/directive/ngRef.js
Outdated
if (attrs.ngRefRead === '$element') { | ||
refValue = element; | ||
} else { | ||
controller = element.data('$' + attrs.ngRefRead + 'Controller'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have two different variables (refValue
, controller
) and not assign to refValue
directly?
src/ng/directive/ngRef.js
Outdated
// when the element is removed, remove it (nullify it) | ||
element.on('$destroy', function() { | ||
// only remove it if value has not changed, | ||
// carefully because animations (and other procedures) may duplicate elements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you remove it carefully? 😁
…o scope Thanks to @drpicox for the original implementation: PR angular#14080
CLAs look good, thanks! |
1 similar comment
CLAs look good, thanks! |
The Travis failures look pretty legitimate to me. |
👍 |
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
feat
What is the new behavior (if this is a feature change)?
publish a component's or element-directive's, or jqlite wrapped DOM element into the containing scope
Does this PR introduce a breaking change?
No
Please check if the PR fulfills these requirements
Other information:
This is a rebased and updated version of #14080 with the following changes:
ngRefElement
attribute that indicates that you always want to publish the jqlite-wrapped DOM element, even if a controller exists