Forcing a digest in Angular - angularjs

At some point after a user action I would like to cause a digest to occur, so the UI reflects a change in the data-model backing it.
I have a service that performs some change in a callback (asynchronously).
I understand that $scope only makes sense in the context of a controller. Can I achieve the same effect by performing $apply() on the $rootScope?
I have seen code that checks for $$phase or similar related to avoiding digest errors, what checks should I perform in order to trigger a digest safely?

See this answer: Running $apply on $rootScope vs any other scope
You can call $rootScope.$apply() outside of a controller (i.e. in a service) in order to trigger a digest loop.
Alternatively, you could consider using $broadcast and $on to send a notification to other parts of your app when something needs refreshing. (See Understanding Angular’s $scope and $rootScope event system $emit, $broadcast and $on)
// in a service
$rootScope.$broadcast('myCustomEvent', {
someProp: 'foobar'
// other data
});
// in a controller or another service
$scope.$on('myCustomEvent', function (event, data) {
console.log(data);
// do something with this event
});

Related

Access parent scope variables in Angular directive [duplicate]

I don't understand how to use $scope.$watch and $scope.$apply. The official documentation isn't helpful.
What I don't understand specifically:
Are they connected to the DOM?
How can I update DOM changes to the model?
What is the connection point between them?
I tried this tutorial, but it takes the understanding of $watch and $apply for granted.
What do $apply and $watch do, and how do I use them appropriately?
You need to be aware about how AngularJS works in order to understand it.
Digest cycle and $scope
First and foremost, AngularJS defines a concept of a so-called digest cycle. This cycle can be considered as a loop, during which AngularJS checks if there are any changes to all the variables watched by all the $scopes. So if you have $scope.myVar defined in your controller and this variable was marked for being watched, then you are implicitly telling AngularJS to monitor the changes on myVar in each iteration of the loop.
A natural follow-up question would be: Is everything attached to $scope being watched? Fortunately, no. If you would watch for changes to every object in your $scope, then quickly a digest loop would take ages to evaluate and you would quickly run into performance issues. That is why the AngularJS team gave us two ways of declaring some $scope variable as being watched (read below).
$watch helps to listen for $scope changes
There are two ways of declaring a $scope variable as being watched.
By using it in your template via the expression <span>{{myVar}}</span>
By adding it manually via the $watch service
Ad 1)
This is the most common scenario and I'm sure you've seen it before, but you didn't know that this has created a watch in the background. Yes, it had! Using AngularJS directives (such as ng-repeat) can also create implicit watches.
Ad 2)
This is how you create your own watches. $watch service helps you to run some code when some value attached to the $scope has changed. It is rarely used, but sometimes is helpful. For instance, if you want to run some code each time 'myVar' changes, you could do the following:
function MyController($scope) {
$scope.myVar = 1;
$scope.$watch('myVar', function() {
alert('hey, myVar has changed!');
});
$scope.buttonClicked = function() {
$scope.myVar = 2; // This will trigger $watch expression to kick in
};
}
$apply enables to integrate changes with the digest cycle
You can think of the $apply function as of an integration mechanism. You see, each time you change some watched variable attached to the $scope object directly, AngularJS will know that the change has happened. This is because AngularJS already knew to monitor those changes. So if it happens in code managed by the framework, the digest cycle will carry on.
However, sometimes you want to change some value outside of the AngularJS world and see the changes propagate normally.
Consider this - you have a $scope.myVar value which will be modified within a jQuery's $.ajax() handler. This will happen at some point in future. AngularJS can't wait for this to happen, since it hasn't been instructed to wait on jQuery.
To tackle this, $apply has been introduced. It lets you start the digestion cycle explicitly. However, you should only use this to migrate some data to AngularJS (integration with other frameworks), but never use this method combined with regular AngularJS code, as AngularJS will throw an error then.
How is all of this related to the DOM?
Well, you should really follow the tutorial again, now that you know all this. The digest cycle will make sure that the UI and the JavaScript code stay synchronised, by evaluating every watcher attached to all $scopes as long as nothing changes. If no more changes happen in the digest loop, then it's considered to be finished.
You can attach objects to the $scope object either explicitly in the Controller, or by declaring them in {{expression}} form directly in the view.
Further readings:
Make Your Own AngularJS, Part 1: Scopes And Digest
In AngularJS, we update our models, and our views/templates update the DOM "automatically" (via built-in or custom directives).
$apply and $watch, both being Scope methods, are not related to the DOM.
The Concepts page (section "Runtime") has a pretty good explanation of the $digest loop, $apply, the $evalAsync queue and the $watch list. Here's the picture that accompanies the text:
Whatever code has access to a scope – normally controllers and directives (their link functions and/or their controllers) – can set up a "watchExpression" that AngularJS will evaluate against that scope. This evaluation happens whenever AngularJS enters its $digest loop (in particular, the "$watch list" loop). You can watch individual scope properties, you can define a function to watch two properties together, you can watch the length of an array, etc.
When things happen "inside AngularJS" – e.g., you type into a textbox that has AngularJS two-way databinding enabled (i.e., uses ng-model), an $http callback fires, etc. – $apply has already been called, so we're inside the "AngularJS" rectangle in the figure above. All watchExpressions will be evaluated (possibly more than once – until no further changes are detected).
When things happen "outside AngularJS" – e.g., you used bind() in a directive and then that event fires, resulting in your callback being called, or some jQuery registered callback fires – we're still in the "Native" rectangle. If the callback code modifies anything that any $watch is watching, call $apply to get into the AngularJS rectangle, causing the $digest loop to run, and hence AngularJS will notice the change and do its magic.
AngularJS extends this events-loop, creating something called AngularJS context.
$watch()
Every time you bind something in the UI you insert a $watch in a $watch list.
User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />
Here we have $scope.user, which is bound to the first input, and we have $scope.pass, which is bound to the second one. Doing this we add two $watches to the $watch list.
When our template is loaded, AKA in the linking phase, the compiler will look for every directive and creates all the $watches that are needed.
AngularJS provides $watch, $watchcollection and $watch(true). Below is a neat diagram explaining all the three taken from watchers in depth.
angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
$scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];
$scope.$watch("users", function() {
console.log("**** reference checkers $watch ****")
});
$scope.$watchCollection("users", function() {
console.log("**** Collection checkers $watchCollection ****")
});
$scope.$watch("users", function() {
console.log("**** equality checkers with $watch(true) ****")
}, true);
$timeout(function(){
console.log("Triggers All ")
$scope.users = [];
$scope.$digest();
console.log("Triggers $watchCollection and $watch(true)")
$scope.users.push({ name: 'Thalaivar'});
$scope.$digest();
console.log("Triggers $watch(true)")
$scope.users[0].name = 'Superstar';
$scope.$digest();
});
}
http://jsfiddle.net/2Lyn0Lkb/
$digest loop
When the browser receives an event that can be managed by the AngularJS context the $digest loop will be fired. This loop is made from two smaller loops. One processes the $evalAsync queue, and the other one processes the $watch list. The $digest will loop through the list of $watch that we have
app.controller('MainCtrl', function() {
$scope.name = "vinoth";
$scope.changeFoo = function() {
$scope.name = "Thalaivar";
}
});
{{ name }}
<button ng-click="changeFoo()">Change the name</button>
Here we have only one $watch because ng-click doesn’t create any watches.
We press the button.
The browser receives an event which will enter the AngularJS context
The $digest loop will run and will ask every $watch for changes.
Since the $watch which was watching for changes in $scope.name
reports a change, it will force another $digest loop.
The new loop reports nothing.
The browser gets the control back and it will update the DOM
reflecting the new value of $scope.name
The important thing here is that EVERY event that enters the AngularJS context will run a $digest loop. That means that every time we write a letter in an input, the loop will run checking every $watch in this page.
$apply()
If you call $apply when an event is fired, it will go through the angular-context, but if you don’t call it, it will run outside it. It is as easy as that. $apply will call the $digest() loop internally and it will iterate over all the watches to ensure the DOM is updated with the newly updated value.
The $apply() method will trigger watchers on the entire $scope chain whereas the $digest() method will only trigger watchers on the current $scope and its children. When none of the higher-up $scope objects need to know about the local changes, you can use $digest().
I found very in-depth videos which cover $watch, $apply, $digest and digest cycles in:
AngularJS - Understanding Watcher, $watch, $watchGroup, $watchCollection, ng-change
AngularJS - Understanding digest cycle (digest phase or digest process or digest loop)
AngularJS Tutorial - Understanding $apply and $digest (in depth)
Following are a couple of slides used in those videos to explain the concepts (just in case, if the above links are removed/not working).
In the above image, "$scope.c" is not being watched as it is not used in any of the data bindings (in markup). The other two ($scope.a and $scope.b) will be watched.
From the above image: Based on the respective browser event, AngularJS captures the event, performs digest cycle (goes through all the watches for changes), execute watch functions and update the DOM. If not browser events, the digest cycle can be manually triggered using $apply or $digest.
More about $apply and $digest:
There are $watchGroup and $watchCollection as well. Specifically, $watchGroup is really helpful if you want to call a function to update an object which has multiple properties in a view that is not dom object, for e.g. another view in canvas, WebGL or server request.
Here, the documentation link.
Just finish reading ALL the above, boring and sleepy (sorry but is true). Very technical, in-depth, detailed, and dry.
Why am I writing? Because AngularJS is massive, lots of inter-connected concepts can turn anyone going nuts. I often asked myself, am I not smart enough to understand them? No! It's because so few can explain the tech in a for-dummie language w/o all the terminologies!
Okay, let me try:
1) They are all event-driven things. (I hear the laugh, but read on)
If you don't know what event-driven is Then
think you place a button
on the page, hook it up w/ a function using "on-click", waiting for
users to click on it to trigger the actions you plant inside the
function. Or think of "trigger" of SQL Server / Oracle.
2) $watch is "on-click".
What's special about is it takes 2 functions as parameters, first one
gives the value from the event, second one takes the value into
consideration...
3) $digest is the boss who checks around tirelessly,
bla-bla-bla but a good boss.
4) $apply gives you the way when you want to do it manually, like a fail-proof (in case on-click doesn't kick in, you force it to run.)
Now, let's make it visual. Picture this to make it even more easy to
grab the idea:
In a restaurant,
- WAITERS
are supposed to take orders from customers, this is
$watch(
function(){return orders;},
function(){Kitchen make it;}
);
- MANAGER running around to make sure all waiters are awake, responsive to any sign of changes from customers. This is $digest()
- OWNER has the ultimate power to drive everyone upon request, this is $apply()

angular controllerAs(this) equivalent of $scope.$broadcast and $scope.on,

According to this answer I just want fire event from controller to another
Call a method of a controller from another controller using 'scope' in AngularJS
$scope.$on("myEvent", function (event, args) {
$scope.rest_id = args.username;
$scope.getMainCategories();
});
And in the second controller you'd just do
$scope.initRestId = function(){
$scope.$broadcast("myEvent", {username: $scope.user.username });
};
But I don't use $scope in my application, just controllerAs and this. There is a way to fire event without inject the scope ? Or should I inject $scope anyway ?But I read in another answer that using both scope and controllerAs is bad practice.
It's not possible to register an $emit or $broadcast event without $scope or $rootScope being injected in the controller.
It is indeed bad practice to use $scope variables and functions since the instance of your controller is already injected inside the $scope with the controllerAs syntax.
But there is not other choice than injecting scope objects if you want to use these events.
However you shouldn't use $emit or $broadcast events just to share data. These events are used for application wide information (like user has logged in or logged out...etc.)
A good practice when using angular events is to prefer $rootScope.$emit because $scope relies on the hierarchy of your components.
For example:
$scope.$emit will emit to the parent component.
$scope.$broadcast will broadcast to children components.
Then $rootScope.$broadcast will broadcast events to the rootScope as well as the scope (which may make your code messy real quick)
$rootScope.$emit is to be preferred as it registers the event application wide and makes it available to the rootScope only. ($rootScope.$on)
Another good practice is to unbind your custom events.
Whenever a component or directive is unloaded/destroyed, the event listener will still reside inside rootScope resulting in possible memory leaks.
To unbind an event:
var unbind = $rootScope.$on('logout', function(event, data) {
console.log('LOGOUT', data);
});
$scope.$on('$destroy', unbind);
I think that generally overuse of $broadcast and $emit is a bad practice too. If you don't want to use the $scope, why not moving the logic handled by events to a service?

AngularJS $scope.$on() and $timeout()

I am new to AngularJS. Please see the code below and tell me what it is doing.
$scope.$on('$viewContentLoaded', function(event) {});
How to use it in a controller to access the DOM?
$timeout(function() { });
I am looking for explanation and example of how to use $scope.$on() and $timeout() in real life and what it does.
$scope.$on registers a listener for the event passed as the first parameter and executes the function passed as the second on each instance of said event. $broadcast and $emit can be used to send out custom events of your own.
$timeout can be used in place of setTimeout but when called with no delay argument will simply wait for the next digest before executing its callback function.
As for DOM manipulation, this should not be carried out in a standard 'jQuery like fashion'. If manipulation of the DOM is required a custom directive can be defined to encapsulate this functionality and therefore allow the Angular framework to govern its syncopation.

AngularJS- why am I needing $scope.$apply within $scope.$on?

I have some code that is not behaving as expected... I have an event listener within a AngularJS controller like this:
$scope.$on("newClipSelected", function(e) {
$scope.$apply(function() {
$scope.isReady = true;
});
});
The controller's markup has a ng-show='isReady'. When this event handler runs, the ng-show region shows as expected. However, this event handler does not work:
$scope.$on("newClipSelected", function(e) {
$scope.isReady = true;
});
With this event handler, if I use the debugger to expect the AngularJS scope I do see that $scope.isReady=true, however the ng-show element is not showing.
Its my understanding that $scope.$on will in fact call $watch/$apply as appropriate. So why am I needing to call $apply in this case?
The event is being triggered by a call to $rootScope.$broadcast() within a non-angularJS asynchronous completion event.
No, angular doesn't fire $apply on the events so if $broadcast is called outside angular's context, you are going to need to $apply by hand.
Check the source here
$on and $broadcast doesn't call $apply for you, you need to call it yourself. I'm not sure why that is, but my guess is that it's because a digest is a bit expensive, and it would be a cost to run a digest cycle for every event.

How do I use $scope.$watch and $scope.$apply in AngularJS?

I don't understand how to use $scope.$watch and $scope.$apply. The official documentation isn't helpful.
What I don't understand specifically:
Are they connected to the DOM?
How can I update DOM changes to the model?
What is the connection point between them?
I tried this tutorial, but it takes the understanding of $watch and $apply for granted.
What do $apply and $watch do, and how do I use them appropriately?
You need to be aware about how AngularJS works in order to understand it.
Digest cycle and $scope
First and foremost, AngularJS defines a concept of a so-called digest cycle. This cycle can be considered as a loop, during which AngularJS checks if there are any changes to all the variables watched by all the $scopes. So if you have $scope.myVar defined in your controller and this variable was marked for being watched, then you are implicitly telling AngularJS to monitor the changes on myVar in each iteration of the loop.
A natural follow-up question would be: Is everything attached to $scope being watched? Fortunately, no. If you would watch for changes to every object in your $scope, then quickly a digest loop would take ages to evaluate and you would quickly run into performance issues. That is why the AngularJS team gave us two ways of declaring some $scope variable as being watched (read below).
$watch helps to listen for $scope changes
There are two ways of declaring a $scope variable as being watched.
By using it in your template via the expression <span>{{myVar}}</span>
By adding it manually via the $watch service
Ad 1)
This is the most common scenario and I'm sure you've seen it before, but you didn't know that this has created a watch in the background. Yes, it had! Using AngularJS directives (such as ng-repeat) can also create implicit watches.
Ad 2)
This is how you create your own watches. $watch service helps you to run some code when some value attached to the $scope has changed. It is rarely used, but sometimes is helpful. For instance, if you want to run some code each time 'myVar' changes, you could do the following:
function MyController($scope) {
$scope.myVar = 1;
$scope.$watch('myVar', function() {
alert('hey, myVar has changed!');
});
$scope.buttonClicked = function() {
$scope.myVar = 2; // This will trigger $watch expression to kick in
};
}
$apply enables to integrate changes with the digest cycle
You can think of the $apply function as of an integration mechanism. You see, each time you change some watched variable attached to the $scope object directly, AngularJS will know that the change has happened. This is because AngularJS already knew to monitor those changes. So if it happens in code managed by the framework, the digest cycle will carry on.
However, sometimes you want to change some value outside of the AngularJS world and see the changes propagate normally.
Consider this - you have a $scope.myVar value which will be modified within a jQuery's $.ajax() handler. This will happen at some point in future. AngularJS can't wait for this to happen, since it hasn't been instructed to wait on jQuery.
To tackle this, $apply has been introduced. It lets you start the digestion cycle explicitly. However, you should only use this to migrate some data to AngularJS (integration with other frameworks), but never use this method combined with regular AngularJS code, as AngularJS will throw an error then.
How is all of this related to the DOM?
Well, you should really follow the tutorial again, now that you know all this. The digest cycle will make sure that the UI and the JavaScript code stay synchronised, by evaluating every watcher attached to all $scopes as long as nothing changes. If no more changes happen in the digest loop, then it's considered to be finished.
You can attach objects to the $scope object either explicitly in the Controller, or by declaring them in {{expression}} form directly in the view.
Further readings:
Make Your Own AngularJS, Part 1: Scopes And Digest
In AngularJS, we update our models, and our views/templates update the DOM "automatically" (via built-in or custom directives).
$apply and $watch, both being Scope methods, are not related to the DOM.
The Concepts page (section "Runtime") has a pretty good explanation of the $digest loop, $apply, the $evalAsync queue and the $watch list. Here's the picture that accompanies the text:
Whatever code has access to a scope – normally controllers and directives (their link functions and/or their controllers) – can set up a "watchExpression" that AngularJS will evaluate against that scope. This evaluation happens whenever AngularJS enters its $digest loop (in particular, the "$watch list" loop). You can watch individual scope properties, you can define a function to watch two properties together, you can watch the length of an array, etc.
When things happen "inside AngularJS" – e.g., you type into a textbox that has AngularJS two-way databinding enabled (i.e., uses ng-model), an $http callback fires, etc. – $apply has already been called, so we're inside the "AngularJS" rectangle in the figure above. All watchExpressions will be evaluated (possibly more than once – until no further changes are detected).
When things happen "outside AngularJS" – e.g., you used bind() in a directive and then that event fires, resulting in your callback being called, or some jQuery registered callback fires – we're still in the "Native" rectangle. If the callback code modifies anything that any $watch is watching, call $apply to get into the AngularJS rectangle, causing the $digest loop to run, and hence AngularJS will notice the change and do its magic.
AngularJS extends this events-loop, creating something called AngularJS context.
$watch()
Every time you bind something in the UI you insert a $watch in a $watch list.
User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />
Here we have $scope.user, which is bound to the first input, and we have $scope.pass, which is bound to the second one. Doing this we add two $watches to the $watch list.
When our template is loaded, AKA in the linking phase, the compiler will look for every directive and creates all the $watches that are needed.
AngularJS provides $watch, $watchcollection and $watch(true). Below is a neat diagram explaining all the three taken from watchers in depth.
angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
$scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];
$scope.$watch("users", function() {
console.log("**** reference checkers $watch ****")
});
$scope.$watchCollection("users", function() {
console.log("**** Collection checkers $watchCollection ****")
});
$scope.$watch("users", function() {
console.log("**** equality checkers with $watch(true) ****")
}, true);
$timeout(function(){
console.log("Triggers All ")
$scope.users = [];
$scope.$digest();
console.log("Triggers $watchCollection and $watch(true)")
$scope.users.push({ name: 'Thalaivar'});
$scope.$digest();
console.log("Triggers $watch(true)")
$scope.users[0].name = 'Superstar';
$scope.$digest();
});
}
http://jsfiddle.net/2Lyn0Lkb/
$digest loop
When the browser receives an event that can be managed by the AngularJS context the $digest loop will be fired. This loop is made from two smaller loops. One processes the $evalAsync queue, and the other one processes the $watch list. The $digest will loop through the list of $watch that we have
app.controller('MainCtrl', function() {
$scope.name = "vinoth";
$scope.changeFoo = function() {
$scope.name = "Thalaivar";
}
});
{{ name }}
<button ng-click="changeFoo()">Change the name</button>
Here we have only one $watch because ng-click doesn’t create any watches.
We press the button.
The browser receives an event which will enter the AngularJS context
The $digest loop will run and will ask every $watch for changes.
Since the $watch which was watching for changes in $scope.name
reports a change, it will force another $digest loop.
The new loop reports nothing.
The browser gets the control back and it will update the DOM
reflecting the new value of $scope.name
The important thing here is that EVERY event that enters the AngularJS context will run a $digest loop. That means that every time we write a letter in an input, the loop will run checking every $watch in this page.
$apply()
If you call $apply when an event is fired, it will go through the angular-context, but if you don’t call it, it will run outside it. It is as easy as that. $apply will call the $digest() loop internally and it will iterate over all the watches to ensure the DOM is updated with the newly updated value.
The $apply() method will trigger watchers on the entire $scope chain whereas the $digest() method will only trigger watchers on the current $scope and its children. When none of the higher-up $scope objects need to know about the local changes, you can use $digest().
I found very in-depth videos which cover $watch, $apply, $digest and digest cycles in:
AngularJS - Understanding Watcher, $watch, $watchGroup, $watchCollection, ng-change
AngularJS - Understanding digest cycle (digest phase or digest process or digest loop)
AngularJS Tutorial - Understanding $apply and $digest (in depth)
Following are a couple of slides used in those videos to explain the concepts (just in case, if the above links are removed/not working).
In the above image, "$scope.c" is not being watched as it is not used in any of the data bindings (in markup). The other two ($scope.a and $scope.b) will be watched.
From the above image: Based on the respective browser event, AngularJS captures the event, performs digest cycle (goes through all the watches for changes), execute watch functions and update the DOM. If not browser events, the digest cycle can be manually triggered using $apply or $digest.
More about $apply and $digest:
There are $watchGroup and $watchCollection as well. Specifically, $watchGroup is really helpful if you want to call a function to update an object which has multiple properties in a view that is not dom object, for e.g. another view in canvas, WebGL or server request.
Here, the documentation link.
Just finish reading ALL the above, boring and sleepy (sorry but is true). Very technical, in-depth, detailed, and dry.
Why am I writing? Because AngularJS is massive, lots of inter-connected concepts can turn anyone going nuts. I often asked myself, am I not smart enough to understand them? No! It's because so few can explain the tech in a for-dummie language w/o all the terminologies!
Okay, let me try:
1) They are all event-driven things. (I hear the laugh, but read on)
If you don't know what event-driven is Then
think you place a button
on the page, hook it up w/ a function using "on-click", waiting for
users to click on it to trigger the actions you plant inside the
function. Or think of "trigger" of SQL Server / Oracle.
2) $watch is "on-click".
What's special about is it takes 2 functions as parameters, first one
gives the value from the event, second one takes the value into
consideration...
3) $digest is the boss who checks around tirelessly,
bla-bla-bla but a good boss.
4) $apply gives you the way when you want to do it manually, like a fail-proof (in case on-click doesn't kick in, you force it to run.)
Now, let's make it visual. Picture this to make it even more easy to
grab the idea:
In a restaurant,
- WAITERS
are supposed to take orders from customers, this is
$watch(
function(){return orders;},
function(){Kitchen make it;}
);
- MANAGER running around to make sure all waiters are awake, responsive to any sign of changes from customers. This is $digest()
- OWNER has the ultimate power to drive everyone upon request, this is $apply()

Resources