AngularJS Internal

38
AngularJS Internal Eyal Vard Microsoft MVP ASP.N blog: eyalvardi.wordpress.c

description

AngularJS Internal

Transcript of AngularJS Internal

Page 1: AngularJS Internal

AngularJS Internal

Eyal VardiMicrosoft MVP ASP.NET

blog: eyalvardi.wordpress.com

Page 2: AngularJS Internal

Startup

HTML

Browser

StaticDOM

DynamicDOM

(View)

AngularJS

DOM Content Loaded Event

ng-app=“module”

$injector

$compile$rootScop

e

$compile (dom,

$rootScope)

Page 3: AngularJS Internal

Angular Bootstrap

(function (window, document, undefined) { 'use strict';

// Functions declerations

jqLite(document).ready(function () { angularInit(document, bootstrap); });

})(window, document);

angularInit

bootstrap

doBootstrap

Page 4: AngularJS Internal

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>

Page 5: AngularJS Internal

Injector

Page 6: AngularJS Internal

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.

Page 7: AngularJS Internal

$injector Methods invoke(fn, [self], [locals])

instantiate(Type, [locals])

get(name)

annotate(fn)

has(name)

Page 8: AngularJS Internal

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');

Page 9: AngularJS Internal

// 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) { }]);

Page 10: AngularJS Internal

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)

Page 11: AngularJS Internal

$injector Internals

$injector

Instance Cache

Provider Injector

get() has()invoke() instantiate() annotate()

get() has()invoke() instantiate() annotate()

Provider Cache

Page 12: AngularJS Internal

Module API

Page 13: AngularJS Internal

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.

Page 14: AngularJS Internal

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

Page 15: AngularJS Internal

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.

Page 16: AngularJS Internal

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);

Page 17: AngularJS Internal

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;});

Page 18: AngularJS Internal

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

Page 19: AngularJS Internal

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

Page 20: AngularJS Internal

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]

Page 21: AngularJS Internal

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

Page 23: AngularJS Internal

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

Page 24: AngularJS Internal

$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

Page 25: AngularJS Internal

$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

Page 26: AngularJS Internal

$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

Page 27: AngularJS Internal

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

Page 28: AngularJS Internal

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.

Page 29: AngularJS Internal

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(); }}

Page 30: AngularJS Internal

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();

Page 31: AngularJS Internal

Scope's $digest() method

$evelAsyncQueue

TraverseScopeLoop

$$postDigest

Page 32: AngularJS Internal

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.

Page 33: AngularJS Internal

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();

Page 34: AngularJS Internal

Communication

Page 35: AngularJS Internal

Communication Between Controllers

Root Scope$on(eventName)

Scope$on(eventName)

Scope$on(eventName)

Scope$on(eventName)

$broadcast

$emit

Page 36: AngularJS Internal

Communication Between Controllers

Page 37: AngularJS Internal

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)

Page 38: AngularJS Internal

Thankseyalvardi.wordpress.com

Eyal VardiMicrosoft MVP ASP.NETblog: eyalvardi.wordpress.com