With angularjs, what is the correct way to get in the view :
{{votedList[{{song.Radio.idSong}}].voted}}
give Error: [$parse:syntax]
song.Radio.idSong is a integer, getting into a ng-repeat
votedList is a scope array
This one:
{{votedList[song.Radio.idSong].voted}}
The double brackets in a view are used for rendering an angular expression, therefore you don't need to interpolate again inside the expression.
Also, scopes inherit the properties of their ancestors (except for isolated scopes) so, as long as you are using that expression inside a scope that has visibility for that property, you are good to go.
Related
When I declare a variable within a javascript method's parenthesis, I am provided with an interesting addition to the function object. However, I cannot actually locate the resulting logic within the function object.
[].forEach(towel = function(){console.log(42)})
console.log(towel) //function forEach.towel()
Could someone please explain the logic behind this unique declaration?
NOTE: This evaluation was all done in the Chrome Dev Console. Perhaps that contributes to the specific syntax returned?
Since you are making an assignment to a variable that has not been declared as var, it leaks to global scope.
The towel is now a variable in global scope.
Is it always safe to use this inside a template to get a reference on the current scope?
<span ng-click="log(this)">
Is my onclick parameter always the current controllers scope?
</span>
Here is a fiddle to show what I mean.
Searched around as well, but couldn't find any point on this in the docs. It seems to work, however I don't want to depend on an undocumented feature. If you can, please link the proper doc for this.
The expression log(this) is evaluated against the scope, and scope object has a this property in it as well which points to its own reference. So it will evaluate this against the scope will yield the scope itself. For example you could also pass $parent and you will see it will point to the parent scope since the argument in the expression gets evaluated against the current scope will yield scope['$parent'] similarly it happens to scope['this'] too. Using ng-click="method(this)" in angular context completely different from doing onclick="somefunc(this)" on a DOM element. Angular is able to expand it only because it has a property with the name this attached to the scope object.
Another thing is you do not need to pass this even otherwise the method is run by evaluating against the scope object, so referring to this will yield the scope itself. i.e you could as well do ng-click="log()" and in the log method you can refer to this as the current scope where the element is bound to. For instance if you were using controllerAs syntax and you are calling:
<div ng-controller="MyOtherCtrl as vm">
<span ng-click="vm.log(this)">Click on me, if background gets teal, then this points on the child controllers scope</span>
</div>
Now this will not be the controller instance (which is what you would generally expect it to be), instead it will just be the scope object itself as it evaluates the expression as scope.vm.log(scope.this)
It would always be safer to use $scope in the context of the controller, unless you specifically want to refer to some child scope created on the view due to some directive(by some ng-repeat, ng-if etc..) wrapping that particular context. Also remember properties get prototypically inherited (except for isolated scoped directive) in the child scopes.
And there are no links documented or anything as far as i know, but source code itself is the official record. Angular uses $parse to expand the expressions and if you see the source code you can see that angular invokes the expressions with function.apply with the context.
this refers to the $scope of the controller directive.
For eg:
<div ng-controller="myCntrl">
<span ng-click="log(this)">
Is my onclick parameter always the current controllers scope?
</span>
</div>
So, this is referring to the myCntrl's $scope.
I have a directive which takes a parameter which can either be a string or an expression which resolves into a string. How can I determine whether it's one or the other?
// directive code around these lines...
$scope.$watch(attr.param, function() {
var evaluatedExpr = $parse(attr.param)(scope);
doSomethingWith(evaluatedExpr);
});
// for a string, I just call
doSomethingWith(attr.param);
If you want to eval a string as an expression use the $scope.$eval method. But, as #ChadRobinson pointed out, this shouldnt be something you need to do in your directive as the expr should probably be evaluated on the parent scope.
As a note to that, unless you are inheriting the parent scope in your directive your expr may not have the information it needs to actually evaluate from within your directive.
$scope.test = function(v){
var expr = $scope.$eval(v);
if(!expr){
console.log("could not eval, maybe a string... who knows?");
}else{
console.log(expr);
}
}
How have you defined this parameter in your scope block? Your choice here determines the answer:
# - This is a one-way binding, so it must be an expression.
& - This binds to a method that executes in the parent' scope, so you would be defining a 'getter' in the parent that the child can call. This may be useful if you are writing a generic library and you want the person using your directive to be able to choose what gets passed. In that case, you would expect that caller to perform any $parse operations required.
= - This makes a two-way binding, which means it must be a property name. It cannot be an expression (or method).
This feeds into your own code example. You are setting a watcher on the parameter. That seems to imply that you're using two-way binding - it wouldn't make sense for a one-way binding because that's only evaluated once. But in that case, you wouldn't call $parse because you'd have the raw value to work with, not an expression.
How are scope attributes being inherited when scope is not set in Angular directive definition?
In Angular docs (under 'Directive Definition Object') there are two cases: when scope is true or when scope is an object ({}). What if scope is not set or is false. How are attributes inherited in this case for the scope and for it's children?
When scope is set to false(also default value) in directive definition, behavior of scope differs based on whether you are doing transclusion or not.
If you do not transclude, it pretty much uses parent scope. Just as if you did not used a directive and written the linked template directly in the page.
Fiddle
Transclusion always creates a child scope but it's bound to parent scope in a strange way when the directive scope is false. You can "read" parent scope but as soon as you "write" to a property it's not bound to parent scope's same property anymore but now a property on child scope unless that property is an object.
Transcluded Fiddle
Child scope is the ones in green border.
Try changing the parent scope first. Enter something in the "var" input field, you will see that the child scope's var will also change. But when you change var in child scope it's not changing the parent scope, and changing the var in parent scope does not affect child scope as the bond is broken when you wrote to var from child scope. This does not apply to the objects (try the same on sp.var, you will see that you cannot break the "bond"). According to developers this is the expected and/or intended behavior.
If the scope is set to false (which is the default) then the directive has the same scope as the parent- no new scope is created. Since they share a scope any changes in the parent will be reflected in the directive and vice-versa.
Since this isn't great from an encapsulation standpoint, many recommend using an isolate scope whenever possible (an isolate scope being when you set the scope to {})
I've a textbox in a controller which is bound to model name. There's a directive inside the controller and there's another textbox inside the directive which is bound to the same model name:
<div class="border" ng-controller="editCtrl">
Controller: editCtrl <br/>
<input type="text" ng-model="name" />
<br/>
<tabs>
Directive: tabs <br/>
<input type="text" ng-model="name"/>
</tabs>
</div>
mod.directive('tabs', function() {
return {
restrict: 'E',
transclude: true,
template:
'<div class="border" ng-transclude></div>',
};
});
When you type something in the outer textbox it's reflected in the inner textbox but if you type something in the inner textbox it stops working i.e. both textbox no more reflects the same value.
See example at: http://jsfiddle.net/uzairfarooq/MNBLd/
I've also tried using two way binding attr (scope: {name: '='}) but it gives syntax error.And using scope: {name: '#'} has same effect.
Any help would be greatly appreciated.
In addition to the accepted answer, this article really helped me in understanding the prototypical inheritance in child scpoes. I'd highly recommend anyone having problem with scopes to read it thoroughly.
A directive with transclude: true results in the directive creating a new (transcluded) child scope. This new scope prototypically inherits from the parent scope. In your case, the parent scope is the scope associated with the editCtrl controller.
Using two-way databinding in a child scope (i.e., ng-model) to bind to a parent scope property that holds a primitive value (e.g., name) always causes problems -- well, I should say that it doesn't work as expected. When the scope property is changed in the child (e.g., you type into the second textbox) the child creates a new scope property that hides/shadows the parent scope property of the same name. If the parent property holds a primitive value, that value is (essentially) copied to the child property when the child property is created. Future changes in the child scope (e.g., the second textbox) only affect the child property.
Before typing into the second textbox (i.e., before the property is changed in the child), the child/transcluded scope finds the name property in the parent scope via prototypal inheritance (dashed line in picture below). This is why the two textboxes initially remain in synch. Below, if you type "Mark" into the first text box, this is what the scopes look like:
I created a fiddle where you can examine the two scopes. Click the "show scope" link next to the second textbox before typing into the second textbox. This will allow you to see the transcluded child scope. You will notice that it does not have a name property at this point. Clear the console, type into the second text box, then click the link again. You will notice that the child scope now has a name property, and the initial value was the value that parent property had ("Mark"). If you typed " likes Angular" into the second text box, this is what the scopes look like:
There are two solutions:
do what #pgreen2 suggests (this is the "best practice" solution) -- use an object instead of a primitive. When an object is used, the child/transcluded scope does not get a new property. Only prototypal inheritance is in play here. In the picture below, assume the editCtrl's $scope has this object defined: $scope.myObject = { name: "Mark", anotherProp: ... }:
use $parent in the child scope (this is a fragile solution, and not recommended, as it makes assumptions about HTML structure): use ng-model="$parent.name" inside the <input> that is within the <tabs> element. The first picture above shows how this works.
A syntax error occurs when using scope: {name: '='} because when using two-way databinding (i.e., when using '='), interpolation is not allowed -- i.e., {{}} can't be used. Instead of <tabs name="{{name}}"> use <tabs name="name">.
Using '#' works the same as the transclude case because ng-transclude uses the transcluded scope, not the isolate scope that is created by using scope: { ... }.
For (lots) more information about scopes (including pictures) see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
I believe that the problem has to do with scoping. Initially the inner textbox doesn't have name set, so it is inherited from the outer scope. This is why typing in the outer box is reflected in the inner box. However, once typing in the inner box occurs, the inner scope now contains name which means it is no longer bound to the outer name so the outer text box doesn't sync up.
The appropriate way to fix is only storing models in the scope, not your values. I fixed it in http://jsfiddle.net/pdgreen/5RVza/ The trick is to create a model object (data) and referencing values on it.
The incorrect code modifies the scope in the directive, the correct code modifies the model in the scope in the directive. This subtle difference allows the scope inheritance to work properly.
I believe the way Miško Hevery phrased it was, scope should be write-only in the controller, and read-only in directives.
update: reference: https://www.youtube.com/watch?v=ZhfUv0spHCY#t=29m19s
Syntax error means that you miswrote something. It is not related to a particular framework / library. You probably forgot to add "," or close a paranthesis. Check it out again