Like the title says, I wonder when the scope variable can not be auto watched? Is that correct that all the scope variable that not specified in template should be manually watched if I want to monitor their value changing?
Thanks
You do not need to $watch any variables or scope properties unless you would like to be notified when they are changed.
Angular implicitly places a $watch on expressions referenced in templates, i.e. {{ x + y }} would place a watch on the result of $scope.x + $scope.y. When these watchers fire, Angular knows to update the view.
All the binded variables are watched by Angular, you don't need to say that you want to watch them, but not all scope variables are watched by Angular.
Watching a variable means that you are gonna be notified when the value of the variable changes. For example, you could show a pop-up when the total is greater than $10. Then you watch the total variable and you perform the action (showing the pop-up).
You can read this question and learn more about watch and Angular: How do I use $scope.$watch and $scope.$apply in AngularJS?
Related
As we all know AngularJS uses the simplest form of $watch when we bind to variable in template like this {{ $ctrl.obj }}.
It should compare value of $ctrl.obj by reference, but if I mutate $ctrl.obj the change is reflected in view. The reference doesn't changed, so why?
When we use brackets ({{}}) angular does not use any watch (ers) for detecting changes on the expression placed inside them. Instead this expression is dirty checked and refreshed in every $digest cycle, even if it is not necessary.
See this post: AngularJS : Why ng-bind is better than {{}} in angular?
I have a input field with model named "name" with some initialized value say "James".
And when i console $scope i can see model value as "James".
ie. $scope.name = "James".
But immediate on next statement if i use setTimeout function and update input field value with Say "Marcus" and console $scope again, why i get $scope.name = "James" only.
I know, i have updated input field value by going out of box than angular and angular is not aware of updation.
So my real question is, Is Angular JS written over Javascript Language? And if yes, why using setTimeout not updated model value in $scope.
Please help me, if i am thinking/question is right or wrong.
This has to do with how angular handles two way data-binding and the digest loop. If you're interested in knowing all the details of how this works then you can read about it here: https://www.ng-book.com/p/The-Digest-Loop-and-apply/
The basic idea is that in angular the digest loop is used to keep track of all the variables you placed in your $scope. Whenever a variable changes it sets off the digest loop, in each cycle of the loop the new value of the variable is compared to the old value and updated accordingly until the new value is equal to the old value which ends the loop. That being said this only works as long as you change your variables within angular's context, this is especially true for changing variables within callback functions or with jquery. Angular doesn't know that a variable was changed so the digest loop never gets started, you could force it by using $scope.$apply but some cases should be avoided in general unless there's no angular alternative for it (Which usually isnt the case).
For setTimeout there's an angular alternative which is $timeout, this has the exact same functionality as setTimeout but it works within angular's context so your variable will be updated.
I have been start learning AngularJS 1.4.7 and I can't understand completely the $scope variables.
Is this a variable inside a Angular controller "like" public variables inside a PHP class?
For example:
I have 2 functions function1(), function2() in HomeCtrl.js and a $scope.img = null; variable, and 2 ng-click event in template file to call these functions.
On ng-cick event i call the function1 and set the value of $scope.url to somethig
Thereafter i call the function2 and console.log($.scope.url) it retutn null. Null is the initialized value of this varaiable.
I'v just set the value of $scope.url in function1 and inside function2 value of variabel is null, why?
I have no idea why happening this.
scope is instance of controller.
When you declare a controller , scope will be avaiable there.
You can access scope through out that controller.
That's why when you change on place in a controller , change will be reflect everywhere in that scope.
Do you literally call $.scope.url? Then that's a problem, it should be $scope.url, no dot between "$" and "scope".
Scope variables have nothing to do with variables accessibility concept (public/private/protected/etc).
They are those "magic" variables, which can be placed inside templates using expressions, which allows data-binding in Angular, i.e., your html with that "magic" scope variables, gets automatically updated by angular, when you just change their values inside your controller, like in that callback function for ng-click. (Actually it's not that easy, here we come to $watch and $digest, but if you are not writing your custom directives, and not mixing, for example, jQuery code with Angular code, you may not think about it for now).
Two-side data-binding, automatic html updates - that's the main point of $scope and $scope variables.
Scope is not and instance of a controller, they are different Angular entities.
I know that both Watchers and Observers are computed as soon as something in $scope changes in AngularJS. But couldn't understand what exactly is the difference between the two.
My initial understanding is that Observers are computed for angular expressions which are conditions on the HTML side where as Watchers executed when $scope.$watch() function is executed. Am I thinking properly?
$observe() is a method on the Attributes object, and as such, it can only be used to observe/watch the value change of a DOM attribute. It is only used/called inside directives. Use $observe when you need to observe/watch a DOM attribute that contains interpolation (i.e., {{}}'s).
E.g., attr1="Name: {{name}}", then in a directive: attrs.$observe('attr1', ...).
(If you try scope.$watch(attrs.attr1, ...) it won't work because of the {{}}s -- you'll get undefined.) Use $watch for everything else.
$watch() is more complicated. It can observe/watch an "expression", where the expression can be either a function or a string. If the expression is a string, it is $parse'd (i.e., evaluated as an Angular expression) into a function. (It is this function that is called every digest cycle.) The string expression can not contain {{}}'s. $watch is a method on the Scope object, so it can be used/called wherever you have access to a scope object, hence in
a controller -- any controller -- one created via ng-view, ng-controller, or a directive controller
a linking function in a directive, since this has access to a scope as well
Because strings are evaluated as Angular expressions, $watch is often used when you want to observe/watch a model/scope property. E.g., attr1="myModel.some_prop", then in a controller or link function: scope.$watch('myModel.some_prop', ...) or scope.$watch(attrs.attr1, ...) (or scope.$watch(attrs['attr1'], ...)).
(If you try attrs.$observe('attr1') you'll get the string myModel.some_prop, which is probably not what you want.)
As discussed in comments on #PrimosK's answer, all $observes and $watches are checked every digest cycle.
Directives with isolate scopes are more complicated. If the '#' syntax is used, you can $observe or $watch a DOM attribute that contains interpolation (i.e., {{}}'s). (The reason it works with $watch is because the '#' syntax does the interpolation for us, hence $watch sees a string without {{}}'s.) To make it easier to remember which to use when, I suggest using $observe for this case also.
To help test all of this, I wrote a Plunker that defines two directives. One (d1) does not create a new scope, the other (d2) creates an isolate scope. Each directive has the same six attributes. Each attribute is both $observe'd and $watch'ed.
<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
attr5="a_string" attr6="{{1+aNumber}}"></div>
Look at the console log to see the differences between $observe and $watch in the linking function. Then click the link and see which $observes and $watches are triggered by the property changes made by the click handler.
Notice that when the link function runs, any attributes that contain {{}}'s are not evaluated yet (so if you try to examine the attributes, you'll get undefined). The only way to see the interpolated values is to use $observe (or $watch if using an isolate scope with '#'). Therefore, getting the values of these attributes is an asynchronous operation. (And this is why we need the $observe and $watch functions.)
Sometimes you don't need $observe or $watch. E.g., if your attribute contains a number or a boolean (not a string), just evaluate it once: attr1="22", then in, say, your linking function: var count = scope.$eval(attrs.attr1). If it is just a constant string – attr1="my string" – then just use attrs.attr1 in your directive (no need for $eval()).
See also Vojta's google group post about $watch expressions.
If I understand your question right you are asking what is difference if you register listener callback with $watch or if you do it with $observe.
Callback registerd with $watch is fired when $digest is executed.
Callback registered with $observe are called when value changes of attributes that contain interpolation (e.g. attr="{{notJetInterpolated}}").
Inside directive you can use both of them on very similar way:
attrs.$observe('attrYouWatch', function() {
// body
});
or
scope.$watch(attrs['attrYouWatch'], function() {
// body
});
I think this is pretty obvious :
$observe is used in linking function of directives.
$watch is used on scope to watch any changing in its values.
Keep in mind : both the function has two arguments,
$observe/$watch(value : string, callback : function);
value : is always a string reference to the watched element (the name of a scope's variable or the name of the directive's attribute to be watched)
callback : the function to be executed of the form function (oldValue, newValue)
I have made a plunker, so you can actually get a grasp on both their utilization. I have used the Chameleon analogy as to make it easier to picture.
Why is $observe different than $watch?
The watchExpression is evaluated and compared to the previous value each digest() cycle, if there's a change in the watchExpression value, the watch function is called.
$observe is specific to watching for interpolated values. If a directive's attribute value is interpolated, eg dir-attr="{{ scopeVar }}", the observe function will only be called when the interpolated value is set (and therefore when $digest has already determined updates need to be made). Basically there's already a watcher for the interpolation, and the $observe function piggybacks off that.
See $observe & $set in compile.js
First and foremost, the plunker: http://plnkr.co/edit/v1uTz5
This is a working demo of the issue I am running into.
I have a ng-include to include a partial.
Inside the partial I have an text input with ngModel AND directive.
The model updates accordingly inside the include, but any interaction outside the include is ignored. The {{test}} outside the include doesn't update, but the {{test}} inside does.
The directive, when called, handles the enter key and calls the correct scope and function. However, the $scope.test variable has never been updated, but $scope.testFinal is updated and the ng-include template renders it appropriately. Trying to reset the $scope.test model does not work either.
Am I missing something here? Or is this a bug with the directive or with the ng-include?
Instead of using a primitiive to define the variable, make it an object.
$scope.model={test:''};
Directives create their own scope for each item. When you equate a primitive to a new scope variable, it has no binding to the original, however when original is an object, a reference is created , not a copy, and changes made in one will reflect in the other
Simple explanatory example:
var a ='foo';
var b= a;
/* now change a*/
a='bar';
alert( b) // is still 'foo'
now do the same with object:
var obj_1= {a:'foo'};
var obj_2=obj_1;
/* now change obj_1.a*/
obj_1.a='bar';
alert( obj_2.a) // change to obj_1 will also change obj_2 and alert returns "bar"*/
Your Plunker Modified
Read this article on angular wiki for more detailed explanation
John Lindquist has a video about it. Although he doesn't quite explains why you need to use an object.
Basically every time there is a new non-isolated scope, every property of the parent scope is copied to the new scope and, as #charlietfl explained, copying a primitive type really creates a "copy" but with objects what you get is a reference, hence the changes are global.
ng-include creates its own scope and it is different than outer scope. Use this.test instead of $scope.test inside ng-include template. It will work properly.