what's the meaning of '=?<name>' in angularJS directive isolate scope declaration? - angularjs

Does the question mark after equals have special meaning?
Example:
scope: {enumMapping: '=?enumMapping'}
I understand '=?' but this is first time I see it. Anyone explain for me.

The question mark (?) means that the value is not required, so 'enumMapping' can be undefined.
The notation 'enumMapping: '=?enumMapping' can be abbreviated to 'enumMapping: '=?' since the name of the attribute is equal to the internal property.
Instead, when the html attribute used it's different from the internal property, it is required to specify it, eg. 'enumMapping: '=?myExternalAttr'
<my-directive my-external-attr=someValue></my-directive>

Related

my-Directive equals a string

I've started to work with dotansimha/angularjs-dropdown-multiselect directive. In the examples he shows the directive is being called out with an equals symbol followed by a undefined double quoted string as shown below.
<div ng-dropdown-multiselect="" options="example13data" selected-model="example13model" extra-settings="example13settings"></div>
My question what is the equals and undefined quotes used for in this example? Is this a way to isolate several of these controls on one page, pass parameters to the directive or does it not serve any purpose and is only a matter of coder style?
You can use this syntax to pass parameters to the directive, and it has some interesting uses. You can read more about it here.
In this particular directive's case, however, it doesn't seem to be doing anything,the documentation for angularjs-dropdown-multiselect does not address it. Given that the attribute parameters could still be accessed if ="" wasn't present, it can be considered style or unnecessary code, perhaps a leftover from a past implementation.

Correct usage of & binding in directives

This has come up for me in answers and generated some discussion and I'd like to figure out if I'm way off base or if there is a "more angular" way to accomplish my goal.
When I write a directive that is going to use an isolated scope, a question that comes up is always =, & or #.
Generally, people always think of & as a way to pass functions to directive. The documentation describes it as:
a way to execute an expression in the context of
the parent scope. If no attr name is specified then the attribute name
is assumed to be the same as the local name. Given and widget definition of scope: {
localFn:'&myAttr' }, then isolate scope property localFn will point to
a function wrapper for the count = count + value expression. Often
it's desirable to pass data from the isolated scope via an expression
to the parent scope, this can be done by passing a map of local
variable names and values into the expression wrapper fn. For example,
if the expression is increment(amount) then we can specify the amount
value by calling the localFn as localFn({amount: 22}).
I use it for passing functions quite a bit, but I also use it in cases where I want to pass an expression that returns a non-string, and I don't want my directive to be able to modify values in the parent scope. In other words, I use it as:
a way to execute an expression in the context of the parent scope
So if I do not need two-way binding, and the expression is not a string, I will do this:
.directive('displayObject', function() {
scope: {
value: '&=displayObject'
},
template: '<div ng-repeat="(k, v) in value()">{{k}}: {{v}}</div>',
replace: true,
...
});
The directive usage would be:
<div displayObject="someScopePropertyOrExpression"></div>
= isn't ideal here because I do not need two way binding. I don't want my directive modifying the value in the parent scope and I don't want the watch required to maintain it.
# isn't ideal because it interpolates the attribute, so value would always be a string.
::someScopePropertyOrExpression doesn't work because I want the directive template to reflect changes in someScopePropertyOrExpression if it changes.
In each discussion, it's always brought up that
ng-repeat="(key, value) in value()"
sets up a watch - the problem is that = and the template together set up two - one that is wholly unnecessary.
Several times when I've suggested this pattern it's been called a "hack", or a misuse of &, and even "ugly".
I don't think it is any of these, but if it is, then my specific question is what is the alternative?
I've searched quite a bit to prove you wrong in our discussion here, but I think you are right - this is the only way to set up a one-way (from parent to isolate scope) binding to an actual model.
In that aspect "&" is superior to "=", because, as you noted, "=" sets up a watch on the parent's scope whereas "&" does not.
One thing - plunker 1 - (that I finally managed to uncover) that hints on it being a hack is that it does not play nice with Angular's one-time binding foo="::name":
"=" will honor the one-time binding (it sets up a watch on the parent and removes it before the next digest), whereas "&" would not:
<input ng-model="name">
<div two-way="::name">
<div one-way="::name">
The other thing - plunker 2 - is that "&" allows to pass local variables, and they may shadow outer variables:
<input ng-model="name">
<div one-way="name">
But in the directive if you did something like: "{{oneWay({name: 'fooo'})}}", it will cause it to display "fooo", rather than take the value of name from the outer scope.
It's a bit like shooting yourself in the foot, yes, but it does hint that this usage might be slightly outside of its original intent.

Difference between isolate scope variables and attr for angularjs directives?

Given:
scope:{
suit: '#',
value: '#'
},
I can access these values in my directive template with {{suit}} or {{value}}
I can also access them in my link function with attr.suit or attr.value.
What happens when I declare class = "{{suit}} {{value}}" in the template and also do elem.addClass(attr.suit) and elem.addClass(attr.value) at the same time?
It seems that the {{}} value takes precedence and overrides what I do in the link function. Can anyone explain why that is? Also, what is the better practice?
At least one consideration is if you expect the values to change in the same directve. Can 'suit' value can be x, and later be changed to y?
If you expect the 'suit' or 'values' to change, considering you're not manipulating them, I'd use them in the template.
ng-class="{{suit}} {{value}}"
(ng-class has better support for interpolations)
If you don't expect 'suit' or 'values' to change, you can remove them as an isolated scope variables since it defines a binding that you don't need, and use elem.addClass(yourAttribute) as you've already written.

Using angular variables inside non-angular attributes

Something I am not understanding about angular is the consistency behind the usage of braces.
say I want to pass in an angular var into an ng-click function:
ng-click="getComments('{{post.Id}}')"
this looks ok in the HTML. but when I click, the parameter is actually still '{{post.Id}}' even though in the html code it was replaced by angular.
also say I want to do this:
<customDirective customAttribute="{{post.Id}}_postID" ></customDirective>
this throws a parsing error and I don't know why. here is the error:
Syntax Error: Token 'post.Id' is unexpected, expecting [:] at column 3 of the expression [{post.Id}}_postID] starting at [post.Id}}_postID].
all the above code is within an ng-repeat.
In the case of ng-click="" whatever is passed between the quotation marks is code, here all variables defined as $scope.variable are simply available as variable, so working code would be
ng-click="getComments(post.id)"
As to the second part, when writing your own directive you specify the attributes to be passed to it like so:
scope: {
attrA: '#',
attrB: '='
}
attrA will bind as a string while attrB will bind as an object, or whatever type variable it may be. For details see this SO answer to ensure you are using the right binding for your use-case.
So your second code sample should read:
<customDirective customAttribute="{{post.Id}}+'_postID'" ></customDirective>
as all text inside the brackets should be seen as code as used when defining any other JS variable.
As to the whole conundrum of when to use which braces in AngularJS, here is a great SO answer on the topic.
Edit:
I have tried out some alternatives for your directive problem, this one works but doesn't update after value changes if that matters:
<customDirective customAttribute="post.Id+'_postID'" ></customDirective>
Though I would suggest that you are approaching a problem with the wrong solution, given that you aren't just interested in figuring out Angular's binding system.
The best solution would be to edit the string in teh directive, or bind an object referencing a function to ensure all changes are propagated:
$scope.post = function(){
return {
id: $scope.someVar.id + "_id"
};
};

Need some examples of binding attributes in custom AngularJS tags

I am attempting to create a custom tag similar to the following:
<mytag type="Big" />
where type is an attribute that gets bound to the component. in such a way that it sets the text in a label, as shown below:
<label>{{type}}</label>
... (other components)...
As the documentation says, I have a controller that sets a default type:
$scope.type = "Small";
so that if I use my tag without the attribute type still gets set.
I am attempting to do binding using a directive:
angular.module('TestPage',[])
.directive('mytag',function() {
return {
restrict: 'E',
templateUrl: 'component.html',
scope: {
type: '='
}
}
});
Note that I do have the appropriate ng-app settings in my component template (ng-app="TestPage").
My problem is that the binding to type does not appear to be actually binding anything.
I have read the documentation about how to bind a variable to components using directive. According to the documentation, you can do such bindings inside a scope. Scopes apparently can contain an "object-hash" (whatever that is!) which creates something called an "isolate scope" (???). Such scopes can represent "local properties" in the following ways:
# or #attr - bind a local scope property to the DOM attribute. The result is always a string
since DOM attributes are strings. If no attr name is specified then the local name and
attribute name are same. Given and widget definition of scope: { localName:'#myAttr' }, then widget scope property localName will reflect the interpolated value of hello {{name}}. As the name attribute changes so will the localName property on the widget scope. The name is read from the parent scope (not component scope).
Huh??? What has all this to do with the proper syntax for binding?
= or =expression - set up bi-directional binding between a local scope property and the parent
scope property. If no attr name is specified then the local name and attribute name are same.
Given and widget definition of scope: { localModel:'=myAttr' }, then widget scope property localName will reflect the value of parentModel on the parent scope. Any changes to parentModel will be reflected in localModel and any changes in localModel will reflect in parentModel.
Excuse me? What is being said here???
& or &attr - provides a way to execute an expression in the context of the parent scope. If no
attr name is specified then the local name and attribute name are same. Given
and widget definition of scope: { localFn:'increment()' },
then isolate scope property localFn will point to a function wrapper for the increment() expression. Often it's desirable to pass data from the isolate scope via an expression and to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).
Now I'm totally confused! You have widget tags and some kind of related function that I have to write iin order to do the bind??? All I want is to bind a value to a label tag!
I have copied the above text from the documentation (http://docs.angularjs.org/guide/directive) to make a point: that this doco reads like the old UNIX documentation: really useful to those who already know the system, but not so helpful to beginners who are trying to develop real expertise. With all the tutorials that show how to do simple tasks in AngularJS (great for toy apps but not so good for the kinds of client- side applications I want to build), why aren't there any for the more advanced stuff???
Okay, time for me to be more constructive.
Can someone please provide some nice, simple examples of how to do the various bindings that this documentation is trying so hard to describe??? Examples that show the proper syntax for these scope statements and descriptions (in plain English) of exactly how they go back to the attribute being added to the custom tag???
Thank you for your patience and thanks in advance for any assistance.
I struggled a bit with this documentation too when first getting into angular, but I will make an attempt try to clarify things for you. First, when using this scope property, it creates an "isolated scope." All this means is that it won't inherit any properties from parent scopes, and so you don't have to worry about any collisions within the scope.
Now, the '#' notation means that the evaluated value in the attribute will automatically get bound into your scope for the directive. So, <my-directive foo="bar" /> would end up with the scope having a property called foo that holds the string "bar". You could also do something like <my-directive foo="{{bar}}" And then the evaluated value of {{bar}} will be bound to the scope. Since attributes are always strings, you will always end up with a string for this property in the scope when using this notation.
The '=' notation basically provides a mechanism for passing an object into your directive. It always pulls this from the parent scope of the directive, so this attribute will never have the {{}}. So, if you have <my-directive foo="bar" /> it will bind whatever is in $scope.bar into your directive in the foo property of your directive's scope. Any change's you make to foo within your scope will be refelected in bar in the parent scope, and vice versa.
I haven't used the '&' notation nearly as much as the other too, so I don't know it as well as those two. From what I understand, it allows you to evaluate expressions from the context of the parent scope. So if you have something like <my-directive foo="doStuff()" />, whenever you call scope.foo() within your directive, it will call the doStuff function in the directive's parent scope. I'm sure there's a lot more you can do with this, but I'm not as familiar with it all. Maybe someone else can explain this one in more detail.
If just the symbol is set in the scope, it will use the same name as the attribute to bind to the directives scope. For example:
scope: {
foo1: '#',
foo2: '=',
foo3: '&'
}
When including the directive, there would need to be the attributes foo1, foo2, and foo3. If you want a property in your scope different than the attribute name, you can specify that after the symbol. So, the example above would be
scope: {
foo1: '#bar1',
foo2: '=bar2',
foo3: '&bar3'
}
When including the directive, there would need to be the attributes bar1, bar2, and bar3, and these would get bound in the scope under properties foo1, foo2, and foo3 respectively.
I hope this helps. Feel free to ask questions with which I can clarify my answer.
Youre pretty close..
app.directive('mytag',function() {
return {
restrict: 'E',
template: '<div>' +
'<input ng-model="controltype"/>' +
'<button ng-click="controlfunc()">Parent Func</button>' +
'<p>{{controlval}}</p>' +
'</div>',
scope: {
/* make typeattribute="whatever" bind two-ways (=)
$scope.whatever from the parent to $scope.controltype
on this directive's scope */
controltype: '=typeattribute',
/* reference a function from the parent through
funcattribute="somefunc()" and stick it our
directive's scope in $scope.controlfunc */
controlfunc: '&funcattribute',
/* pass a string value into the directive */
controlval: '#valattribute'
},
controller: function($scope) {
}
};
});
<div ng-controller="ParentCtrl">
<!-- your directive -->
<mytag typeattribute="parenttype" funcattribute="parentFn()" valattribute="Wee, I'm a value"></mytag>
<!-- write out your scope value -->
{{parenttype}}
</div>
app.controller('ParentCtrl', function($scope){
$scope.parenttype = 'FOO';
$scope.parentFn = function() {
$scope.parenttype += '!!!!';
}
});
The magic is mostly in the scope: declaration in your directive definition. having any scope: {} in there will "isolate" the scope from the parent, meaning it gets it's own scope... without that, it would use the parent's scope. The rest of the magic is in the scope's properties: scope: { 'internalScopeProperty' : '=externalAttributeName' }... where the = represents a two way binding scenario. If you change that = to a # you'll see it just allows you to pass a string as an attribute to the directive. The & is for executing functions from the parent scope's context.
I hope that helps.
EDIT: Here is a working PLNKR

Resources