AngularJS Internal
-
Upload
eyal-vardi -
Category
Software
-
view
3.661 -
download
0
description
Transcript of AngularJS Internal
AngularJS Internal
Eyal VardiMicrosoft MVP ASP.NET
blog: eyalvardi.wordpress.com
Startup
HTML
Browser
StaticDOM
DynamicDOM
(View)
AngularJS
DOM Content Loaded Event
ng-app=“module”
$injector
$compile$rootScop
e
$compile (dom,
$rootScope)
Angular Bootstrap
(function (window, document, undefined) { 'use strict';
// Functions declerations
jqLite(document).ready(function () { angularInit(document, bootstrap); });
})(window, document);
angularInit
bootstrap
doBootstrap
Manual Initialization
<html ><body> Hello {{'World'}}! <script src="angular.js"></script> <script> angular.element(document).ready(function () { angular.module('myApp', []); angular.bootstrap(document, ['myApp']); }); </script></body></html>
Injector
Modules and the Injector There is a single injector per Angular application.
The injector provides a way to look up an object instance by its name.
$injector Methods invoke(fn, [self], [locals])
instantiate(Type, [locals])
get(name)
annotate(fn)
has(name)
Create an Injector From Module// Create a modulevar myModule = angular.module('myModule', [])
// Configure the injectormyModule.factory('serviceA', function () { return { // instead of {}, put your object creation here };});
// create an injector and configure it from 'myModule'var $injector = angular.injector(['myModule']);
// retrieve an object from the injector by namevar serviceA = $injector.get('serviceA');
// always true because of instance cache$injector.get('serviceA') === $injector.get('serviceA');
// inferred (only works if code not minified/obfuscated)$injector.invoke(function (serviceA) { });
// annotatedfunction explicit(serviceA) { };explicit.$inject = ['serviceA'];$injector.invoke(explicit);
// inline$injector.invoke(['serviceA', function (serviceA) { }]);
The Real Magic of The Injector
// You write functions such as this one.function doSomething(serviceA, serviceB) { // do something here.} // Angular provides the injector for your applicationvar $injector = ...; ///////////////////////////////////////////////// the old-school way of getting dependencies.var serviceA = $injector.get('serviceA');var serviceB = $injector.get('serviceB'); // now call the functiondoSomething(serviceA, serviceB); ///////////////////////////////////////////////// the cool way of getting dependencies.$injector.invoke(doSomething)
$injector Internals
$injector
Instance Cache
Provider Injector
get() has()invoke() instantiate() annotate()
get() has()invoke() instantiate() annotate()
Provider Cache
Module API
Module API
Config ( function( xxxProvider ){} )
Registration- controller(name, constructor)- directive(name, directiveFn)- filter(name, filterFactory)
Registration ($provide)
- service(name, constructor)- factory(name, providerFn)- provider(name, providerType)- decorator(name, fn )
- constant(name, object)- value(name, object)
run(initializationFn)
Execute when the injector is done loading all modules.
ngXXX
Modules Load Cycle
angular.module('myApp', ['ngXXX', 'ngYYY']);
Constant
Provider
ngYYY
Constant
Providers
myApp
Constant
Providers
Config Config Config
Run Run Run
$injector
Instance Cache
ProviderCache
Loading and Dependencies The Config Block
Registers all the providers in this phase.
Only providers and constants can be injected into Config blocks.
Services that may or may not have been initialized cannot be injected.
The Run Block Run blocks are used to kickstart your
application, and start executing after the injector is finished creating.
Only instances and constants can be injected into Run blocks.
Directive Internal
<div ng-controller="MyCtrl"> <ul> <li ng-repeat="n in names">{{n}}</li> </ul></div>
$compile()
First the HTML is parsed into DOM using the standard browser API.
Once all directives for a given DOM element have been identified they are sorted by priority and their the directive compile() functions are executed.
DOM + link($scope)
Live binding between the scope and the DOM
Register any listeners on the elements and set up any watches with the scope.
var $compile = ...; // injected into your codevar scope = ...; var html = '<div ng-bind="exp"></div>'; // Step 1: parse HTML into DOM elementvar template = angular.element(html); // Step 2: compile the templatevar linkFn = $compile(template); // Step 3: link the compiled template with the scope.linkFn(scope);
Directive Definition Object (DDO)var myModule = angular.module(...); myModule.directive('directiveName', function factory(injectables) { var directiveDefinitionObject = { priority: 0, template: '<div></div>', // or function templateUrl:'directive.html', replace: false, transclude: false, restrict: 'A', scope: false, require: '^?ngModel' controller: function($scope, $element, $attrs, $transclude, Injectables) { ... }, compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } }, link: function postLink(scope, iElement, iAttrs, controller) { ... } };
return directiveDefinitionObject;});
DDO Execution Order
<div directive1
directive2>
<div directive3>
Hello World...
</div>
</div>
$compile start
$compile end
Factory func
Template Compile Controller preLink postLink
Factory func
Template Compile Controller preLink postLink
Factory func
Template Compile Controller preLink postLink
The Compile Functionfunction compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) { ...
var compositeLinkFn = compileNodes( compileNodes, transcludeFn, $compileNodes, maxPriority, ignoreDirective, previousCompileContext);
...
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers) { ... }; }
Create all the DDO’s Execute all DDO’s template property or
function Execute all DDO’s compile functions
Execute all DDO’s controllers Execute all DDO’s preLink
functions Execute all DDO’s postLink
functions
The compileNodes Functionfunction compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, previousCompileContext) { ... for (var i = 0; i < nodeList.length; i++) { attrs = new Attributes();
directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, ignoreDirective);
nodeLinkFn = (directives.length) ? applyDirectivesToNode(directives, nodeList[i], attrs, ...)
: null; ...
childLinkFn = (nodeLinkFn ...) ? null : compileNodes( childNodes , ...); ... } ... }
Scan the DOM (DFS) and find all directives
Sort the directive by priority
Execute the directive factory and store the DDO
Call to the DDO.template Call to the DDO.compile
Execute the compileNodes on the child nodes of nodeList[i]
Step II : The Link Functions function bootstrap(element, modules) { ...
function(scope, element, compile, injector, animate) { scope.$apply(function() { element.data('$injector', injector); compile(element)(scope);});...
}Execute the
compile processExecute the link process
<div directive1
directive2>
<div directive3>
Hello World...
</div>
</div>$compile start
$compile end
Factory func
Template Compile Controller preLink postLink
Factory func
Template Compile Controller preLink postLink
Factory func Template Compile Controller preLink postLink
DDO Execution Order
Ng-repeat Execution Order
Factory func Template Compile
Controller preLink postLink
<ul> <li ng-repeat="x in [1,2,3,4]" directive-name> {{x}} </li></ul>
x 4
$parse Service Parsing an Angular Expression Converts Angular expression into a
function.var parseFn = $parse(' expression
');
var resultValue = parseFn($scope);
// Set value to expression
var setter = parseFn.assign;
setter(context,value);
Do in compile
$interpolate Service Compiles a string with markup into an
interpolation function, no directive.
var temp = $interpolate( "{{a}}+{{b}}=<b>{{ result }}</b>" );
var result = temp( {a: '2', b: '3', result: '5'} );
Do in compile $parse $parse $parse
$compile
$interpolate
$compile Service Compiles an HTML string or DOM into a
template and produces a template function, which can then be used to link scope and the template together.
$parse
Live DOM
<!-- Expressions --> Please type your name : {{name}}
<!-- Directives & Data Binding -->
Name: <input ng-model="name" value="..." />
Template
name :
Scope valu
e
elm.bind('keydown', … )
$scope.$watch('name', … )
Directive
Binding
Runtime
Native
Event Queue
(wait)
DOM Render
JavaScript
AngularJS
Event
Loop
$eval Async queue
$watch list
$apply(fn)
fn()
$digest loop
During the runtime phase: Pressing an 'X' key causes the browser to emit a keydown event on the
input control.
The input directive captures the change to the input's value and calls $apply("name = 'X';").
Angular applies the name = 'X'; to the model.
The $digest loop begins.
The $watch list detects a change on the name property and notifies, which in turn updates the DOM.
Angular exits the execution context.
The browser re-renders the view with update text.
When The Expression Updated? When the ngModel is update the Scope is
calling to $apply('name=...').
$apply() is used to execute an expression in angular from outside of the angular framework.
// Pseudo-Code of $apply()function $apply(expr) { try { return $eval(expr); } catch (e) { $exceptionHandler(e); } finally { $root.$digest(); }}
Observing Model Changes ($watch) $watch(watchFn, watchAction,
deepWatch) watchFn - This parameter is a string with an Angular
expression or a function that returns the current value of the model that you want to watch.
watchAction - This is the function or expression to be called when the watchFn changes.
deepWatch - If set to true, this optional boolean parameter tells Angular to examine each property within the watched object for changes. ...var dereg = $scope.$watch('Model.Property', callbackOnChange());…// de-register $watchdereg();
Scope's $digest() method
$evelAsyncQueue
TraverseScopeLoop
$$postDigest
Scope's $digest() method Processes all of the watchers of the
current scope and its children.
Because a watcher's listener can change the model, the $digest() keeps calling the watchers until no more listeners are firing.
This means that it is possible to get into an infinite loop. This function will throw 'Maximum iteration limit exceeded.' if the number of iterations exceeds 10.
Counter = 0
Counter = 1
Scope's $digest() methodscope.name = 'misko';scope.counter = 0;
scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1;});
scope.$digest();
scope.name = 'adam';
scope.$digest();
Communication
Communication Between Controllers
Root Scope$on(eventName)
Scope$on(eventName)
Scope$on(eventName)
Scope$on(eventName)
$broadcast
$emit
Communication Between Controllers
Scope Type
Scope
Properties: $id
Events: $destroy
Lifecycle Methods $destroy() $new(isolate
)
Communication Methods: $broadcast(name,
args) $emit(name, args) $on(name, listener)
Runtime Methods: $watch(…)
$apply(exp) $digest()
$eval(exp) $evalAsync(e
xp)
Thankseyalvardi.wordpress.com
Eyal VardiMicrosoft MVP ASP.NETblog: eyalvardi.wordpress.com