AngularJS is taking the web development world by storm. People quickly learn how to create simple controllers, directives, filters, services and such without really understanding how AngularJS works underneath the hood. Two key concepts to really understanding AngularJS are $scope and the $digest loop. This presentation will examine $scope as it relates to JavaScript prototype inheritance, the HTML DOM, controllers, and directives. Next, we will demystify the $digest loop explaining how AngularJS updates $scopes, executes $watches and keeps up with our changing data models.
3. $scope – two parent-child relationships
lPrototype inheritance
lChild scopes can isolated or prototypally inherit from its parent
scope in the hierarchy
lHierarchy
l$parent property to reference the parent
lChild Nodes are organized in a linked list instead of an array for
performance reason because they are frequently created and
destroyed and array modification routines are more expensive than
updating references in a linked list
l$childHead – first child
l$childTail – last child
5. Why prototype inheritance?
lAll of functionality needed for a $scope is attached to the
$rootScope, and made available via prototype inheritance to all
non-isolated child $scopes
lNote: isolation refers to prototype inheritance, not the linked heirarchy
of $scopes
lPrototype inheritance is preferred over a closure because it is
more efficient and uses less memory
6. Isolated $scope
lNormally, scopes will prototypally inherit from their parent in the
hierarchy
lHowever, isolated scopes can be created that have a $parent
reference, but they do not inherit prototypally from their parent or
any other scope
lSome people think an isolated $scope inherits from the $rootScope,
this is not true
lCode demo to examine child scope which are and are not
isolated
7. When to isolate and when not to isolate...
lIn AngularJS,
lScopes created by ng-controller always prototypally inherit
lScopes created using $new will prototypally inherit unless true is
passed in
lPrimarily, used when assigning a $scope to transcluded content
lScopes created by directives can prototypally inherit or be
isolated
lAlways prefer an isolated scope for maximum directive reusability
8. $digest Loop
lChild $scope traversal
lDepth first, starting from the $scope object from which it is called
lDoes not traverse up from the $scope object from which it is called
lPrototypal inheritance (isolated scopes) does not impact the $digest
loop traversal process
lIt follows the linked hierarchy of the scopes not the prototypal inheritance
hierarchy
lCode Demo
9. $digest Loop TTL
lWhenever a value on the $scope is changed during the $digest
loop, it causes the $digest loop to execute again
lThe $digest loop will trigger itself up to 10 (by default) times before it
stops and throws an error to prevent an infinite loop
lIf you need to increase this value, you can do so in a config function,
by passing in the $rootScopeProvider and calling digestTtl function
and passing in the new TTL value
lNote – only increase this value if you really know what you are doing as it can
adversely impact the performance and user experience of your AngularJS
application
lCode Demo
10. $apply
lProvides a try/catch/finally structure to properly handle errors
lReview AngularJS Source Code
lDemo Try/Catch/Finally
lPasses the function argument to the $eval function on the
$scope from which $apply is called
lCan be called without passing a function to simply safely call the
$digest function
lAfter executing $eval, $digest on the $rootScope is called
11. $$postDigest & $timeout
lTo execute code one time after a completed $digest cycle there
are two methods
l$$postDigest
lNot recommended, the double $ means its a private function to AngularJS
lSimply pass a function to the method and it will be executed after the $digest
loop and it will not trigger another $digest loop
lStores functions in a queue that are executed at the end of the $digest Loop
after the $digest phase has been cleared
lAdvantage – no delay between $$postDigest functions and completion of
$digest phase
12. $$postDigest & $timeout
l$timeout
l0 for delay and false for invokeApply
lPublic method, returns a promise
lMust be called during the $apply or $digest to queue up to execute after the
$digest loop has completed
lLimitation – because of the single threaded nature of JavaScript, it will be
queued up after the last task in the JavaScript event loop which could mean it
could possibly not be executed immediately after the $digest loop completes
13. $digest & $apply Summary
lSimilarity
lBoth trigger the $digest loop
lDifferences
l$digest starts from the $scope on which it is called
l$apply executes the $digest from the $rootScope
lAdditionally
l$apply executes the expression on the $scope from which it is called
using $eval
l$apply provides error handling, calling $digest on the $rootScope from
a finally block
14. $watches and $observes
lBoth are executed during the $digest loop
l$watches can be configured in controllers, $observes cannot be
lBoth $watches and $observes can be configured in directives,
usually, within the post-link function
l$watches can only observe a property on the scope, $observes
only observe an attribute of an element on which a directive is
applied but it can observe an interpolated expression
15. $watches and $observes
l$watches require move overhead then $observes, as such,
$observes are preferred over $watches
l$watches require the execution of a conditional function that
checks to see if the value has changed, then it executes the
listener function
lThe conditional function must execute for each watcher; therefore, do
not place multiple watchers on the same conditional function, place on
watcher and have it handle multiple things
l$observes simply observe an interpolated attribute value, if it
changes then all of the $observe functions exectute
lThis requires less overhead than a $watch
16. Best Practices
- Always, prefer an isolated $scope for directive to ensure loose
coupling with the surrounding $scope and directive reusability
- Use $apply instead of $digest directly unless you have a large
scope model and need to limit the number of child scopes the
$digest function is executed against
- Limit the use of $watches and $observes to UI affecting events
and when possible choose an $observe over a $watch
- Never $watch the same $scope property or function more than
once