Don't understand the syntactical inconsistencies in Angular - angularjs

A few examples:
<img ng-src="{{some.thing}}">
<button ng-class="{active: data.checkThing()}">
<div ng-repeat="thing in things"></div>
As someone learning Angular, this is highly confusing. I never know whether I need { } or {{ }} or neither. Can someone explain what these mean and when to use which?

{{}}:
You use {{}} when you want to evaluate an expression, such as:
$scope variables, e.g:
$scope.myVar = "test";
And in HTML:
<div>{{myVar}}</div> will result in <div>test</div>
Basic calculations, such as: {{ 1 + 1 }}, {{ myVar + " add on" }}
Result of a function on the controller so it can be displayed
Etc..
{}:
Condition, used directives such as ng-class/ng-style. It means the left-hand side will be in effect if the expression on the right-hand side evaluates to true. The following expression means that the button will have the class "active", if data.checkThing() evaluates to true:
<button ng-class="{active: data.checkThing()}">
Neither:
When you want to address objects, like you do in the ng-repeat, you have to iterate on objects, like you do in C#/Javascript/etc..
So because you're working with the objects themselves, you don't need to evaluate anything, and simply address them as they are variables on your scope:
<div ng-repeat="item in items">...</div>
If you are using directives for instance, and you have an isolated scope and want to bind a variable in that scope to an instance, you will use no brackets:
<div myDirective item="myItem"></div>
And in the directive you can have a reference to that item by doing:
angular.module('app').directive('my-directive', function () {
return {
scope: {
item: "=" //references the object that exists in
//attribute `item` on the DOM element the directive is on
}
}
});

Single braces are used for directives accepting an object parameter, which may be more than one value. It is analogous to a JSON object.
For example:
ng-class="{active: true, highlight:true}"
Double braces are used to represent an AngularJS expression.
For example
<div>{{ item.name }}<div> (evaluates to string)
<div>{{ item.name == "Sam" }}<div> (evaluates to boolean)
No braces are used for directives accepting a single value argument.
For example, data binding:
ng-model="people.jim"

{active:data.checkThing()} signifies object creation-----you are constructing and actually passing an object to ng-class directive in the angularjs library.
{{some.thing}} signifies 2-way data binding with the actual ng-model that reflects the changes in model data as and when it occurs.
Hope it helps.If this answers your queries kindly accept this as answer or comment back if not.

Related

What is the diff between using {{...}} and without {{...}} and angular directives?

Actually I'm confused between when to use {{ }} when using angular directives and when to not to use {{ }}
For example:
<div data-ng-init="isHidden=false">
<div data-ng-show="isHidden">
...
</div>
</div>
and
<div data-ng-init="isHidden=false">
<div data-ng-show="{{isHidden}}">
...
</div>
</div>
I'm confused between these syntax ? What are the differences between those? And when to use what? Thanks in advance :)
There is no difference except the "look" u need to use the {{value}} syntax in case you want to write data anywhere in your html body
<div>{{value}}</div>
It's all explained here: Difference between double and single curly brace in angular JS?
For quick answer:
{{}} are Angular expressions and come quite handy when you wish to
write stuff to HTML
Don't use these at a place that is already an expression!
For instance, the directive ngClick treats anything written in between
the quotes as an expression
<div data-ng-init="isHidden=false">
<div data-ng-show="isHidden">
...
</div>
</div>
In This Situation data-ng-show = false , Takes From data-ng-init As Statically,if You Have Given true Then It Returns True .
But Here
<div data-ng-init="isHidden=false">
<div data-ng-show="{{isHidden}}">
...
</div>
{{}} Called As Expressions In Angular One Of The Most Important Concept
It Directly Evaluate If isHidden = true Or False Based On Any Condition Written In Your App.js File .
Example:
<div data-ng-init="isHidden=YourVariable">
<div data-ng-show="{{isHidden}}">
...
</div>
if(YourVariable == true){
Do Somthing
}
else{
Do Something
}
If you are asking when to use {{}} while assigning value to a attribute and when not.
It depends on the binding types of directive. '#' or '='
So here, you have to use:
data-ng-show="{{isHidden}}" if the binding type of directive scope data-ng-show is '#', that mean the data-ng-show will be expecting a string value. So in this case if you keep data-ng-show="isHidden" it will take data-ng-show's value as 'isHidden', but data-ng-show="{{isHidden}}" will take the value of the $scope.isHidden and assign to data-ng-show.
Now if the binding type of directive scope data-ng-show is '=', that means the data-ng-show will be expecting a value from a scope. So data-ng-show="isHidden" itself will take the value of he $scope.isHidden and assign to data-ng-show.
Note: all the default HTML attributes expect a string so you have to use {{}} for default HTML attributes.
There is no as such major difference unless one uses them in the DOM for the value.
When one uses the following:
<div data-ng-show="isHidden">
then, expression is evaluated and on the basis of it respective value, the ng-show either hides or displays the div. But the value of the isHidden cannot be seen, when one inspects the HTML using the browser developer tool.
When one uses the following:
<div data-ng-show="{{isHidden}}">
In this case, the value of the isHidden can be seen from the developer tools, and the rest of the expression does evaluates the same as that of (1).

When to use double braces {{}} in angularJS

Taken from the Angular documentation:
Angular Expressions
Angular expressions are JavaScript-like code snippets that are mainly
placed in interpolation bindings such as
<span title="{{ attrBinding }}">{{ textBinding }}</span>
but also used directly in directive
attributes such as ng-click="functionExpression()".
For example, these are valid expressions in Angular:
1+2 a+b user.name items[index]
However I'm a little confused as to when to use the double braces syntax {{}} and when not to. The documentation seems to suggest that you don't need them when using expressions within the directive attributes (see the ng-click example above).
Although the following code which works offers anecdotal evidence to the contrary:
<ul id="Menu">
<li ng-repeat="appModule in applicationModules"
id="{{appModule.Name}}"
ng-class="{ 'selected' : selectedAppModule == '{{appModule.Name}}' }"
ng-click="menuClicked(appModule.Name)">
{{appModule.Display}}
</li>
</ul>
Note how in the ng-class directive the double braces are used and inside the ng-click directive they are not.
How do you know when to use them and when not to?
It depends on the directive attribute in question and the type of binding it uses. Further more it depends on what you intend in the given situation.
From your example:
ng-repeat="appModule in applicationModules"
No need for the braces as this expression is evaluated by angular inside the ng-repeat directive.
id="{{appModule.Name}}"
Here we need braces as we want the id to be equal to the value of the expression.
ng-class="{ 'selected' : selectedAppModule == '{{appModule.Name}}' }"
I'm pretty sure this can be written as:
ng-class="{ 'selected' : selectedAppModule == appModule.Name }"
And you get the same behaviour.
ng-click="menuClicked(appModule.Name)"
In this example we need the ng-click to be bound to the method named menuClicked.
Generally we use {{}} when we want to evaluate the expression and when dealing with attributes we don't always need to use {{}} as they are in many cases evaluated behind the scenes.
Simple Tip A rule of thumb for when {{}} is needed is by thinking of it as a wrapper for a .ToString()-method. Does converting the expression to a string make sense, then so does using {{}}. (Any counter examples are very welcome)
Check the documentation. Avoid using using interpolation {{ }} when
the documentation says that the directive takes an expression, . In the case of ng-src, the documentaion explicitly says use {{ }}. If the attribute is not an AngularJS directive, use interpolation.
Erroneous
ng-class="{ 'selected' : selectedAppModule == '{{appModule.Name}}' }"
The above example is an example of mixing interpolation and Angular epressions.
Instead use:
ng-class="{ 'selected' : selectedAppModule == appModule.Name }"
From the Docs:
Why mixing interpolation and expressions is bad practice:
It increases the complexity of the markup
There is no guarantee that it works for every directive, because interpolation itself is a directive. If another directive accesses attribute data before interpolation has run, it will get the raw interpolation markup and not data.
It impacts performance, as interpolation adds another watcher to the scope.
Since this is not recommended usage, we do not test for this, and changes to AngularJS core may break your code.
— AngularJS Developer Guide - mixing interpolation and expressions
Update
Don't use interpolation with:
ng-selected, see AngularJS ng-selected Directive API Reference
ng-disabled, see AngularJS ng-disabled Directive API Reference
ng-required
ng-if
ng-show
ng-hide
ng-open
ng-value
ng-repeat
ng-options

Use ng-repeat's $index variable within an AngularJs expression

I have an ng-repeat div that I need to use the $index variable to build out an expression. Consider the following:
<div ng-repeat="item in myItems track by $index">
This will throw an error: {{myItemId_{{$index}}.someProperty}}
</div>
If I have a $scope variable called "myItemId_0", how do I use $index within a curly braces {{}} expression? I have tried omitting the curly braces for the $index variable, this but this doesn't work either:
{{myItemId_$index.someProperty}}
Since it's a variable on scope, create a function that retrieves your property via bracket notation.
HTML:
{{getByIndex($index, "someProperty")}}
Controller:
$scope.getByIndex = function(index, someProperty) {
var propertyName = "myItemId_" + index;
return $scope[propertyName][someProperty];
}
However, the logical solution is to not access your properties in this way at all, but rather use item which is already available from your ng-repeat.
HTML:
div ng-repeat="item in betterItems track by $index">
{{item.someProperty}}
</div>
Controller:
$scope.betterItems = [];
angular.forEach(myItems, function (i) {
$scope.betterItems.push(createMappedItem(i));
}
Even if you have to map your collection to a new collection (or reuse the same if need be), you can still access that original item. It's cleaner and much easier to maintain. In addition, retrieval by index can be very dangerous if you start mutating the collection.

Why ngIf has higher priority than {{ }} (interpolate)?

What is the reasoning behind setting ngIf priority (600) higher than {{ }}(100)? Shouldn't it have a lower priority to allow {{ }} inside ng-if attribute value?
I would like to have a condition inside a $scope variable:
Controller:
app.controller('MainCtrl', function($scope, $http, $parse) {
$scope.hide = "check === 'hidden'";
$scope.parsecond = function (cond) {
return $parse(cond)($scope);
};
});
Template:
<body ng-controller="MainCtrl">
<div ng-if="!{{hide}}">funky ng-if div</div>
<div ng-hide="{{hide}}">ng-hide div</div>
<div ng-if="!parsecond(hide)">ng-if div</div>
<input type="input" ng-model="check" />
</body>
ng-hide works fine since it parses the contents of the hide variable and returns "check === 'hidden'" which then gets evaluated by ng-hide directive.
But ng-if tries to evaluate {{hide}} before interpolate has had a chance to parse the string hence ng-if throws an exception.
The only solution I've found is call a function that basically does the job of the interpolate directive and returns the parsed content.
Plnkr showing the issue: link
EDIT:
After reading documentation I've found better way of doing it without the need of a custom method on the $scope since angularjs has already a method that parses a variable against its current $scope ($eval).
So my solution would be:
<body ng-controller="MainCtrl">
<div ng-if="!$eval(hide)">funky ng-if div</div>
<div ng-hide="{{hide}}">ng-hide div</div>
<div ng-if="!parsecond(hide)">ng-if div</div>
<input type="input" ng-model="check" />
</body>
Updated plnkr: link
Although this still doesn't explain why ngIf has higher priority.
EDIT 2:
Just so people understand that it's not the same:
For example:
Controller:
$scope.value = "hi";
$scope.condition = "value === 'bye'";
HTML:
<div ng-hide="condition"></div> <!--This will be evaluated to true since !!"value ==='bye'" = true. -->
<div ng-hide="{{condition}}"></div> <!--This will be evaluated to false since value !== 'bye' = false -->
<div ng-if="condition"></div> <!--This will be evaluated to true since !!"value === 'bye'" = true. -->
<div ng-if="{{condition}}"></div> <!--This will give an exception since ngIf directive ran before interpolation directive in the $compile step. -->
<div ng-if="$eval(condition)"></div> <!--This will be evaluated to false since value !== 'bye' = false. -->
My conclusion is that it safer to use $parse if you want the directive to evaluate/set a watch in the string rather than in the property on the scope. Although it's true that I could use {{ }} for ng-hide/ng-show or any directive that has a lower priority than 100 but I'm guessing it's not safe since I'm depending in the compiling order and it's not 100% clear that it won't change in future patches.
ng-if expects its value to be an angular expression - under the hood it just makes use of $scope.$watch. Therefore, if you want to condition including content of ng-if on some variable defined on the scope (let say: scope.hide), you put ng-if="hide" in your mark-up. No double curly braces here.
Now back to your question: it is true that ng-if has priority of 600, but $interpolate is angular's service - not a directive. As such $interpolate does not define priority. Where did you get 100 from?
UPDATE
You can always condition including content of ng-if on some function (let say scope.conditionFn) by putting in your mark-up: ng-if="conditionFn()".
UPDATE 2
I updated your PLNKR to make it working. The inconsistencies between ng-if and ng-hide in your plunker had nothing to do with priority of interpolation taking place in $compile.
UPDATE 3
It seems that you are right that order of interpolation plays a role here, but... I really do not see any good reason to interpolate inside of angular's expression. The reason why ng-if has relatively high priority is that it removes/adds transcluded content from/to DOM, whereas ng-hide just shows/hides the transcluded content. I think it is a pure coincidence that one directive seems to work and the other not. But if you do not use unnecessary, superfluous tricks, both do work as intended, what my plunker shows.
To understand why it's doing that, observe the console of this sample here, where the custom directive, similar to ngIf, sits this time at priority 0. The directive is meant to remove the element and, without waiting, to add it back. You'll see however an error which is caused by the attempt to set the attributes back onto what remained due to transclude: elementwhich, in this case and that of the ngIf, is just a marker for where the element has been, in the form of a comment.
To avoid that from happening, ngIf terminates the process early by having a higher priority, terminal:true and by monitoring directly its expression grabbed straight from $tAttrs. Interpolation will execute, but this is done at a later stage, by calling the transclusion function the moment the ngIf expression turns true, on a clone of the original element, now under its control. The new element will show nice and dandy under the comment element.
Here is the same sample but fixed. The error condition is avoided.

why do some angular examples have single handlebars and some have double handlebars?

Please note Double {{ }} :
http://docs.angularjs.org/api/ng.directive:ngMouseleave
Please note single Single: { }
http://docs.angularjs.org/api/ng.directive:select
Does it have something to do with "in the quotes" or "in the tag?" Please confirm. And does any one know why?
Of course the vast level of info on the handlebars site does not help:
The double braces are for one-way binding a model to the template, essentially stick the value of the model into that location.
The single braces signify what type of value the attribute expects. For example:
<select
ng-model="{string}"
[name="{string}"]
[required]
[ng-required="{string}"]
[ng-options="{comprehension_expression}"]>
</select>
ng-model expects a string
name (optional) expects a string
required (optional) expects no value
ng-required (optional) expects a string
ng-options (optional) expects an Angular comprehension expression
The string values can be Angular expressions
Versus this, which is just a direct binding:
<body>
<button ng-mouseleave="count = count + 1" ng-init="count=0">
Increment (when mouse leaves)
</button>
count: {{count}}
</body>
Where the value count on the scope replaces {{count}}
Both ways you provide a expression which are like javascript expressions.
HTML attributes that have been extended by angularjs and take an expression can take any expression without using interpolation symbol {{}}. Single {} is just to signify what should be provided.
Everywhere else you use interpolation {{}}
See documentation on expressions here http://docs.angularjs.org/guide/expression

Resources