As digest cycle do the dirty checking of the variable that is if there are 100 scope variables and if I change one variable then it will run watch of all the variables.
Suppose I have 100 scope model variables that are independent of each other. If I make changes in one variable then I don't want to check all other 99 variables. Is there any way to do this ? If yes, how ?
Surprisingly, this is usually not a problem, Browsers don’t have problems even with thousands of bindings, unless the expressions are complex. The common answer for how many watchers are ok to have is 2000.
Solutions :
It is fairly easy onwards from AngularJS 1.3, since one-time bindings are in core now.
One time Binding of the variables.
We can use One time binding(::) directive to prevent the watcher to watch the unwanted variables. Here, variable will be watch only once & after that it will not update that variable.
Stop the digest cycle manually.
HTML :
<ul ng-controller="myCtrl">
<li ng-repeat="item in Lists">{{lots of bindings}}</li>
</ul>
Controller Code :
app.controller('myCtrl', function ($scope, $element) {
$element.on('scroll', function () {
$scope.Lists = getVisibleElements();
$scope.$digest();
});
});
During the $digest, you are only interested in changes to Lists object, not changes to individual items. Yet, Angular will still interrogate every single watcher for changes.
directive for stop and pause the digest:
app.directive('stopDigest', function () {
return {
link: function (scope) {
var watchers;
scope.$on('stop', function () {
watchers = scope.$$watchers;
scope.$$watchers = [];
});
scope.$on('resume', function () {
if (watchers)
scope.$$watchers = watchers;
});
}
};
});
Now, Controller code should be changed :
<ul ng-controller="listCtrl">
<li stop-digest ng-repeat="item in visibleList">{{lots of bindings}}</li>
</ul>
app.controller('myCtrl', function ($scope, $element) {
$element.on('scroll', function () {
$scope.visibleList = getVisibleElements();
$scope.$broadcast('stop');
$scope.$digest();
$scope.$broadcast('resume');
});
});
Reference Doc : https://coderwall.com/p/d_aisq/speeding-up-angularjs-s-digest-loop
Thanks.
This is a good question and highlights one of the biggest deficiencies with Angular 1.x. There is little control over how the digest cycle is managed. It is meant to be a black box and for larger applications, this can cause significant performance issues. There is no angular way of doing what you suggest, but There is something that would help you achieve the same goals (ie- better performance of the digest cycle when only one thing changes).
I recommend using the bind-notifier plugin. I have no relationship with the project, but I am using it for my own project and have had great success with it.
The idea behind is that you can specify certain bindings to only be $digested when a specific event has been raised.
There are multiple ways of using the plugin, but here is the one that I find must effective:
In a template file, specify a binding using the special bind-notifier syntax:
<div>{{:user-data-change:user.name}}</div>
<div>{{:job-data-change:job.name}}</div>
These two bindings will not be dirty-checked on most digest cycles unless they are notified.
In your controller, when user data changes, notify the bindings like this:
this.refreshUserData().then(() => {
$scope.$broadcast('$$rebind::user-data-change');
});
(and similar for job-data-changed)
With this, the bindings for user.name will only be checked on the broadcast.
A few things to keep in mind:
This essentially subverts one of the key benefits of angular (also it's core weakness for large applications). Two way binding usually means that you don't need to actively manage changes to your model, but with this, you do. So, I would only recommend using this for the parts of your application that have lots of bindings and cause slowdowns.
$emit and $broadcast themselves can affect performance, so try to only call them on small parts of the $scope tree (scopes with few or no children).
Take a good look at the documentation since there are several ways to use the plugin. Choose the usage pattern that works best for your application.
This is quite a specific use-case to do exclusive/conditional checks in the digest cycle and I don't think it is possible without forking/hacking the angular core.
I would consider refactoring how/what you are $watching. Perhaps using ngModelController's $viewChangeListeners would be more suitable than $watch?
Related
I have a directive that abstracts a menu. The menu item selection needs to be notified to its parent controller so it can take required action. There are multiple ways I could get this achieved.
Pass a scope variable from controller to directive and observe the change on this variable. Within the directive change this variable to indicate the selection option.
Pass a callback method from controller to directive. Invoke the callback from directive upon change.
Observe the changes in controller using $scope.$on and notify from the directive using scope.$emit
I could not clearly arrive at which one option is better. I am leaning towards option 3 as it seems to be cleaner but I am not sure if this has a unwanted coupling. I would like to hear an opinion from others, which solution would favour clear dependency and good for testability.
UPDATE:
After reading the suggestions and thought, I picked up Option 2 for below reasons:
It is very obvious by looking into the HTML about the dependency
<menu save="onSave()" filterByDate="filterByDate(date)"></menu>
Unit testing is very explicit and tell about the API (interface) the directive
I personally don't like using observe\emit\on unless i have too. It isn't always obvious looking at someone elses code where it is being set. This can lead to spaghetti code. If you have a call back, i find the direct link is more obvious to the eye and easier to find. This is in conjunction with well named properties etc. At the end of the day it's a matter of personal\team taste.
I recommend using a modified version of 1. But instead of passing a variable, inject a service wherever you need.
The service can look something like this:
yourApp.factory('SelectedMenu',
function () {
//set a default or just initialize it
var menuItem = {
id: 1,
code:"x"
};
return {
getId: function () { return menuItem.id; },
getCode: function() { return menuItem.code;},
setId: function(newId){menuItem.id = newId},
setCode: function(newCode){menuItem.code=newCode;},
};
}
);
In the directive(s) which controlls the behavior create a watch:
$scope.$watch(
function() {
//should be idempotent! can execute multiple times per $digest cycle when a change is detected
// (good compromise for not polluting $rootScope)
return SelectedMenu.getId();
},
function( newValue) {
//do your thing...
}
);
When you need to use it/update the value you can set the code or any other property you want and finally set the id to trigger the update.
SelectedMenu.setId(someones.id);
It is the cleanest solution i came across. If you want, you can abstract the usage even more with another service if the data object is more complex.
I'm following thinksters angular nfl fantasy tutorial here http://www.thinkster.io/angularjs/eHPCs7s87O/angularjs-tutorial-learn-to-rapidly-build-real-time-web-apps-with-firebase and they implement the ng-class active in this way...
<li data-ng-repeat="entry in navbarEntries" data-ng-show="auth" data-ng-class="{ active: entry.isActive }">
$scope.$on('$routeChangeSuccess', function() {
$scope.navbarEntries.forEach(
function(data) {
data.isActive = ($location.path().indexOf(data.link) == 0);
}
)
})
They have this explanation for the implementation below...
"Why, might you ask, wouldn't you just call a method from the view, which would assess if it should be active or not?
The reason is the $digest cycle re-evaluation. This cycle occurs very, very often, and every method call you use in your view to assess property will be re-invoked every single cycle, which will obviously degrade performance as the application scales."
I'm wondering what it would look like to "call a method from the view", what "to assess property" means, and would like a bit more of an explanation in what cases to use like this to avoid $digest cycle re-evaluation is appropriate in angular.
To call a method from the view would look something like this:
<li ng-class="{active: urlIsActive('/home/')}">Home</li>
Then urlIsActive would be a function in the scope, like this:
$scope.urlIsActive = function(url) {
return ($location.path().indexOf(url) == 0);
}
The problem with this approach is that the function urlIsActive would have to be re-evaluated on every scope digest cycle, wasting a lot of performance. The approach they suggest is using events to manipulate scope variables instead, which is cheaper in terms of performance.
Note that the suggested scope variable manipulation occurs when an event is fired. When rendering the page, finding out if a link should be active is just a simple variable lookup, rather than actually executing a function (which has a lot of overhead in itself).
This is an angularjs app. I have a service that handles the loading of content (ajax). While the service is getting the content, a number of things throughout the app hide, later showing again (depending on the content returned). They might have the same scope, different scope, whatever. They just need to hide while content is loading, and then show when it's done. Pretty normal stuff.
Right now, I have separate controllers watching a "loading" property of the service and using regular angular directives (ng-show, ng-hide, etc.) to show/hide. But this feels like overkill. I'd prefer to write a custom "loading" directive that injects the loading service and does the watching and showing/hiding.
My question is: Is what I want to do "bad"? The controller way, I end up boilerplating a bunch of code, maybe up to like 5 or 6 times, or even more as the app grows. The custom directive way, I write it once and use an attribute where I need it. Yeah - there's a dependency on that service, but that just doesn't feel like the end of the world that some people have made me start to think I should think it is.
For what it's worth, I feel like I've heard "separation of concerns" so many times I've become paralyzed by it. It leads me to overthink everything because I want to do things the right way, but it sure doesn't feel like I'm being very productive.
If I understood correctly, you have a bunch elements that should hidden when a particular service is loading data, and then be displayed again when the data is loaded, right?
In that case, events might be a good solution:
they can be global to the appliciation (which i think is what you are aksing for).
they allow for avoiding direct coupling between elements (also one of your concerns).
So, in your service, just broadcast events when stuff happens:
$rootScope.$broadcast('loading-data');
axajStuffOrWhatever(function() {
$rootScope.$broadcast('data-loaded');
});
Then, wrap the show/hide behaviour in a directive that will listen to those events.
.directive('hideWhileLoadingData', function() {
return {
link: function(scope, el, attrs) {
scope.$on('loading-data', function() {
el.css('display', 'none');
});
scope.$on('data-ready', function() {
el.css('display', 'block');
});
}
};
});
Use the directive:
<div hide-while-loading-data>something</div>
The advantage of using events here, is that later on, they could be originated by a different service, or by multiple services, and the directive will not be affected by that as long as the events are the same.
For more complex behaviour, you could also parametrize the events and the directive, so different elements will react to different kind of stuff.
I've made an example of this.
In my opinion all scopes which depend on this service should be children of one parent scope. If you have the parent scope responsible for talking with the service then any directive of any scope can access it via $parent on the $scope.
I'm evaluating whether or not to use AngularJS for a web project, and I'm worried about the performance for a feature I need to implement. I would like to know if there's a better way to implement the functionality I'm trying to in AngularJS.
Essentially, it seems to me the time it takes AngularJS to react to an event is dependent on the number of DOM elements in the page, even when the DOM elements aren't being actively changed, etc. I'm guessing this is because the $digest function is traversing the entire DOM.. at least from my experiments, that seems to be the case.
Here's the play scenario (this isn't exactly what I'm really trying to do, but close enough for testing purposes).
I would like to have angularJS highlight a word as I hover over it. However, as the number of words in the page increases, there's a larger delay between when you hover over the word and when it is actually highlighted.
The jsfiddle that shows this: http://jsfiddle.net/czerwin/5qFzg/4/
(Credit: this code is based on a post from Peter Bacon Darwin on the AngularJS forum).
Here's the HTML:
<div ng-app="myApp">
<div ng-controller="ControllerA">
<div >
<span ng-repeat="i in list" id="{{i}}" ng-mouseover='onMouseover(i)'>
{{i}},
</span>
<span ng-repeat="i in listB">
{{i}},
</span>
</div>
</div>
</div>
Here's the javascript:
angular.module('myApp', [])
.controller('ControllerA', function($scope) {
var i;
$scope.list = [];
for (i = 0; i < 500; i++) {
$scope.list.push(i);
}
$scope.listB = [];
for (i = 500; i < 10000; i++) {
$scope.listB.push(i);
}
$scope.highlightedItem = 0;
$scope.onMouseover = function(i) {
$scope.highlightedItem = i;
};
$scope.$watch('highlightedItem', function(n, o) {
$("#" + o).removeClass("highlight");
$("#" + n).addClass("highlight");
});
});
Things to note:
- Yes, I'm using jquery to do the DOM manipulation. I went this route because it was a way to register one watcher. If I do it purely in angularJS, I would have to register a mouseover handler for each span, and that seemed to make the page slow as well.
- I implemented this approach in pure jquery as well, and the performance was fine. I don't believe it's the jquery calls that are slowing me down here.
- I only made the first 500 words to have id's and classes to verify that it's really just having more DOM elements that seems to slow them down (instead of DOM elements that could be affected by the operation).
Although an accepted answer exists allready, I think its important to understand why angularJS reacts so slow at the code you provided. Actually angularJS isnt slow with lots of DOM elements, in this case it's slow because of the ng-mouseover directive you register on each item in your list. The ng-mouseover directive register an onmouseover event listener, and every time the listener function gets fired, an ng.$apply() gets executed which runs the $diggest dirty comparision check and walks over all watches and bindings.
In short words: each time an element gets hovered, you might consume e.g. 1-6 ms for the internal
angular dirty comparision check (depending on the count of bindings, you have established). Not good :)
Thats the related angularJS implementation:
var ngEventDirectives = {};
forEach('click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(name) {
var directiveName = directiveNormalize('ng-' + name);
ngEventDirectives[directiveName] = ['$parse', function($parse) {
return {
compile: function($element, attr) {
var fn = $parse(attr[directiveName]);
return function(scope, element, attr) {
element.on(lowercase(name), function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
};
}
};
}];
}
);
In fact, for highlighting a hovered text, you probably would use CSS merely:
.list-item:hover {
background-color: yellow;
}
It is likely that with newer Angular Versions, your code as is, will run significantly faster. For angular version 1.3 there is the bind-once operator :: which will exclude once-binded variables from the digest loop. Having thousands of items, exluded will reduce the digest load significantly.
As with ECMAScript 6, angular can use the Observe class, which will make the dirty comparisiion check totaly obsolete. So a single mouseover would result internally in a single event callback, no more apply or diggesting. All with the original code. When Angular will apply this, I dont know. I guess in 2.0.
I think that the best way to solve performance issues is to avoid using high level abstractions (AngularJS ng-repeat with all corresponding background magic) in such situations. AngularJS is not a silver bullet and it's perfectly working with low level libraries. If you like such functionality in a text block, you can create a directive, which will be container for text and incapsulate all low level logic. Example with custom directive, which uses letteringjs jquery plugin:
angular.module('myApp', [])
.directive('highlightZone', function () {
return {
restrict: 'C',
transclude: true,
template: '<div ng-transclude></div>',
link: function (scope, element) {
$(element).lettering('words')
}
}
})
http://jsfiddle.net/j6DkW/1/
This is an old question now, but I think it's worth adding to the mix that Angular (since v1.3) now supports one time binding which helps slim down the digest loop. I have worked on a few applications where adding one time binding reduced the number of watches dramatically which led to improved performance.
ng-repeat is often responsible for adding a lot of watches, so you could potentially consider adding one time binding to the ng-repeat.
ng-repeat="i in ::list"
Here is a summary of a few techniques that can be used to avoid adding unnecessary watches
http://www.syntaxsuccess.com/viewarticle/547a8ba2c26c307c614c715e
Always profile first to find the real bottleneck. Sometimes it might be not something you initially suspect. I would suspect your own code first, then Angular (high number of watchers being the main feature leading to sluggish performance). I described how to profile and solve different performance problems in an Angular app in detailed blog post https://glebbahmutov.com/blog/improving-angular-web-app-performance-example/
Get zone.js from btford and run all functions in a zone to check their times, then create zones to handle ajax code (crud) and other zones for static code (apis).
Alternatively, limiting ng-repeat and/or disabling two-way binding on objects goes a long way atm.. a problem that web components already covers by using shadow dom leaving the top crispy to touch. still zone.js - watch the video through the link on plausibilities.
Well, I can see you are using $watch. Angular recomends $watch whenever it is very much needed. SCenarios like updating a variable through ng-model
http://jsfiddle.net/5qFzg/10/
Suraj
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()