Angularjs Why I cannot directive scope variable "dataSource" - javascript

Noticed strange thing, when I name directive scope parameter dataSource it always undefined.
Here is an example: http://plnkr.co/edit/F0wIVUTj9lavVZyFIxKn?p=preview
If you change dataSource to for example ds everything works.
Question: why?

As you can read here, angular normalizes all attributes, a standard prefix for attributes is data-. So when you name your attribute data-source, it will actually be bound to your directive as source
Your corrected fiddle code
Now can be accessed as $scope.source in your directive.

Related

Data binding in directive isolated scope

I want to create a directive with isolated scope, but I'm not able to get it working.
jsFiddle
I want to isolate age model in a directive scope. I want to perform some business logic on that model and then set that model to parent binding. I hope the fiddle is explanatory.
I am also adding a button to the template which when clicked should invoke a submit function:
<button ng-click="submit()">click me</button>
It seems the button is working fine, but why is $scope.$watch() is not begin triggered? In a normal situation, if I change the view value it will automatically update the model value. But now it isn't.
$watch requires a dollar sign, and you pass either a function or a string that is evaluated on your scope, i.e.:
$scope.$watch('age', function(value) {
There are many more errors in your code, for instance you don't have a declared variable called 'age' so this line will reference window.age and give you an error because it is undefined, you need to say $scope.age I think:
age = age+10;
It just looks like your updated fiddle is a playground, hope these point you in the right direction. I'd recommend going through the egghead.io angular videos.

Angular ngModel binding on first level method of $scope

This is not exactly a question with some code, but more for my understandings. So please forgive me if this isnt appropriate here..
I was playing with a simple checkbox with an ngModel assigned:
<input type="checkbox" ng-model="someFlag"/>
I expect this to bind the boolean of the checkbox to $scope.someFlag (if controller and everything else configured properly).
And yes, it does work. But there are times where I found that this is not working. Case: When I try to do something when the someFlag changes (for example in a $watch) the value is not really binded.
But then I came accross something a collegua at work mentioned once:
Use a wrapper object
Doing it like that works now without any problems:
<input type="checkbox" ng-model="wrapperObject.someFlag"/>
Watching $scope.wrapperObject.someFlag works as expected.
Now the question: Why??
When ngModel directive is executed, it reads attribute's ng-model value (in this case, "someFlag") and saves it in it's local function scope (not to confuse to angulars $scope). But since a boolean in javascript is a primitive, you cannot pass it by reference, only by value. That means only the $scope.someFlag's value (true or false) get's copied to ngModel, and not the way of accessing and modifying $scope.someFlag from ngModel.
When you use wrapper object, it is passed by reference, meaning, it is the same thing both in $scope.wrapperObject and in ngModel's local function scope, because it points to the same memory address behind the scenes.
I'm not sure if this explanatory enough, but when working with primitive values in angular, you must keep in mind the difference between passing by reference and by value and that when primitive's change, other parts of application might not know this.
In my understanding you want to pass Boolean value if check box checked to controller
try this:
In your html:
<input type="checkbox" ng-model="value"
ng-true-value="YES" ng-false-value="NO"> <br/>
in your controller:
$scope.value is gives Boolean value//
This is because scope inheritance. A nice article about angularjs scope and inheritance
I guess the $watch logic you mentioned might have been triggered from inside a ng-repeat or a ng-if. Remember angularjs creates a new scope variable for each object inside ng-repeat and ng-if.
<input type="checkbox" ng-model="someFlag"/> //This is a primitive $scope.someFlag
<input type="checkbox" ng-model="obj.someFlag"/> //This is an object $scope.obj.someFlag
<div ng-repeat="opt in options">
Here a new child scope - $scope.somechild is created for the opt variable.
So if primitive variable someFlag is referenced here it will be taken as $scope.child.someFlag. so any update here will not update the parent $scope.someFlag.
But if an object obj.someFlag is referenced here by object inheritance the compiler will try to find obj.someFlag in child - $scope.child as it is not present it will search in parent $scope as it is present this will now refer to $scope.obj.someFlag and any modification done here is done in the actual parent scope.
This is part of the mantra: "there should always be a dot in your model" by Misko Hevery, one of the fathers of Angular. You can, and probably should, watch this video: https://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=32m51s
ngModel is a directive that does two-way-data-binding. Binding to a primitive (Boolean in this case) will make the setter to set it on the current scope, rather than on the scope that's defined which could interfere with other scopes.
In your scenario, in a parent scope we have your $scope.someFlag = true. In a child scope we then have your input:
<input type="checkbox" ng-model="someFlag"/>
That will work initially but once the user changes the value, the someFlag will be created on the child scope and the bindings will read and write from that value from now on.
I hope this is, somehow, clear.
Note that this only happens with two-way-data-binding directives and not regular ones such as ngDisabled or ngHide

Angular Isolate Scope and Attributes

In the following plunk:
http://plnkr.co/edit/ss3HTb?p=info
I am attempting to bind an attribute to an isolate scope. However, it is not working. I'm wondering if anyone knows why that is?
I've looked through several resources, including:
Need some examples of binding attributes in custom AngularJS tags and http://www.sitepoint.com/practical-guide-angularjs-directives-part-two/
You specified the isolate scope as
scope: {
attr: "=attributeName"
}
What that does is take the value of attributeName specified in your html as an expression in your scope and bind that to attr in your directive scope. But what you did was just to specify "hello" without having a hello object in your scope. To get it to work the way you have it now, you have to use the # attribute binding like so:
scope: {
attr: "#attributeName"
}
The other change that you need to do is how you specify the value in the html. You use titleCase when you define the attribute, but in the html instead of titleCase you need to seperate the "words" by a dash and use all lowercase. So attributeName becomes attribute-name
<node attribute-name="hello"></node>
On the other hand, if you actually want to bind to the value of an object in your scope, then make sure that you specify it in your scope and then you can use it in your directive (and preferably use the dot notation).
I have you updated your plunker to show you how they work.

Angularjs Binding 2 directives scope models to a parent controllers models

I have a form controller with 2 object models model1:{name:"foo"} model2:{name:"model2"}
I created 2 directives (both which create isolated scopes) . One with Element only binding which uses model1 and another one with Attribute only binding which uses model2.
The nesting is like so:
<div myattibute="model2">
<mytag my-model="model"></mytag>
</div>
The attribute only directive doesn't have a template and the tag directive has a template.
The problem is that I am getting undefined in mytag directive for the model.
1.Can someone see the problem and explain it in the plnkr ?
http://plnkr.co/edit/Q23XqY?p=preview
Partial Solution:
A working example with adding an empty div template with just ng-transclude for the myattribute directive makes it work. With i mandated that this attribute directive is on a div it that I would have wanted it to be placeable on any div, span etc.
Here is the working example:
http://plnkr.co/edit/z0M5ys?p=preview
2.How is ng-transclude affecting scope inheritance?
3.Can't I create this attribute with only business logic without any markup ?
Isolate scopes are best avoided except for rare cases they add unnecessary complexity. It is much simpler to just use $scope.$watch to bind to expressions in attributes like this:
$scope.$watch(attrs.myModel, function(newValue, oldValue) {})
$scope.$watch(attrs.myattribute, function(newValue, oldValue) {})
This way your directives can either share the parent scope they were declared in and handle binding to it using $watch expressions, or they can create a child scope using { scope: true } if needed.
Here is one possible solution: http://plnkr.co/edit/mm2q67?p=preview
Keep in mind that your myTag directive can use an isolate scope if you'd really like to do it that way, but the myattribute one can't as that will break the scope inheritance chain for myTag

In AngularJS, how to make an isolated scope inherit from ng-repeat's scope

I'm trying to create a custom component that receives arguments in a ng-repeat loop.
So for example, say I have a component named "mycomp" that receives a custom argument "name" in a ng-repeat:
<mycomp name="{obj.name}" ng-repeat="obj in list" />
And in my directive the isolated scope is defined like this:
scope:{name:"#"}
That won't work because ng-repeat creates an isolated scope for each element it iterates. So I ended up having two levels of scopes.
How do I get around this issue? Am I doing something wrong?
Thanks.
As I stated in my comment of your original question, this has already been answered. Anyway, here it is, summed up:
In your template, state the model you want to have inherited, without {{}} (as using brackets results in the value being passed, and not the reference to the model itself):
<mycomp name="obj.name" ng-repeat="obj in list" />
And in your directive, establish a 2-way binding, like so:
scope:{name:"="}
EDIT:
I realize now (after your comment) that while this solves your problem, it doesn't fully answer the question. Here goes:
When you create a directive you have the choice of creating a scope that inherits from its parent (controller, typically, though not necessarily) ou an "isolated" scope, by specifying scope: true or scope: {...}, respectively.
So, by creating an unisolated scope, all the parent's models are available (you can access scope.obj - created via ng-repeat - but also scope.list). This is convenient, but also dangerous, of course (and doesn't really create reusable code).
If you create an isolated scope, you can specify the scope's models using '#', '=' or '&'.
'#' and '&' both produce a isolated, unbinded value, (that, if you change, changes only on the isolated scope - in your case, the object in the original list suffers no change at all), the only difference being that '#' reads a string value, and '&' reads an expression.
THIS IS IMPORTANT: the reason why I believe your code didn't work was (only) because you passed name="{obj.name}" and not name="{{obj.name}}", for with '#' the string value is read, and that string value can be the name of obj, but you must include it in {{}}!
If you use '=', you are declaring that you want that variable to be binded with the specified outside variable. So, if (in a fit of crazy, crazy rage!) you want to have 2 models in your directive that start up with the same value, but on is binded (i.e. changes are propagated to the outside scope), you could do something like this:
<mycomp binded-name="obj.name" unbinded-name="{{obj.name}}" ng-repeat="obj in list" />
and in your directive:
scope:{
bindedName: "=",
unbindedName: "#"
}

Categories