Inside The AngularJS Directive Compiler Tero Parviainen @teropa
Directives “Angular attempts to minimize the impedance mismatch between document centric HTML and what an application needs by creating new HTML constructs. Angular teaches the browser new syntax through a construct we call directives.” https://docs.angularjs.org/guide/introduction
<body ng-app="myApp"> <main-navigation> </main-navigation> <login-form orientation="vertical"> </login-form> <news-feed max-items="10"> </news-feed> </body>
{ priority: 0, terminal: false, template: '<div></div>', templateNamespace: 'html', replace: false, multiElement: false, transclude: false, restrict: 'A', scope: false, controller: 'MyCtrl', controllerAs: 'myCtrl', bindToController: true, require: '^parentCtrl', compile: function(el) { return { pre: function(scope, el, attrs) { }, post: function(scope, el, attrs) { } } } }
src/ng/compile.js •  1307 lines of code •  84 functions •  Compilation & linking •  Attribute management •  Controllers •  Isolate bindings •  Templates & transclusion https://github.com/es-analysis/plato
Learning by Imitation
1.  Compilation 2.  Linking 3.  Inheritance 4.  Isolation
1.  Compilation 2.  Linking 3.  Inheritance 4.  Isolation
The Directive Compiler $compile Directives + DOM Compiled DOM
$compileProvider.directive('myClass', function() { return { compile: function(element) { element.addClass('decorated'); } }; }); Directive Registration
<div id="root"> <div my-class></div> </div> Directive Usage
var root = document.querySelector('#root'); var $root = angular.element(root); $compile($root); Compilation
The Compile Provider function $CompileProvider() { this.directive = function(name, factory) { }; }
Directive Registration function $CompileProvider() { var directives = {}; this.directive = function(name, factory) { directives[name] = directives[name] || []; directives[name].push(factory()); }; }
Constructing $compile function $CompileProvider() { var directives = {}; this.directive = function(name, factory) { directives[name] = directives[name] || []; directives[name].push(factory()); }; this.$get = function() { return function $compile(element) { }; }; }
The compileNode helper function this.$get = function() { function compileNode(element) { } return function $compile(element) { compileNode(element); }; };
Collecting Directives this.$get = function() { function collectDirectives(element) { } function compileNode(element) { var directives = collectDirectives(element); } return function $compile(element) { compileNode(element); }; };
Three Collection Strategies this.$get = function() { function collectDirectives(element) { return collectElementDirectives(element) .concat(collectAttrDirectives(element)) .concat(collectClassDirectives(element)); } function compileNode(element) { var directives = collectDirectives(element); } return function $compile(element) { compileNode(element); }; };
Element Directives function collectElementDirectives(element) { var elName = element[0].nodeName; var directiveName = _.camelCase(elName); return directives[directiveName] || []; }
Attribute Directives function collectAttrDirectives(element) { var result = []; _.each(element[0].attributes, function(attr) { var dirName = _.camelCase(attr.name); result = result.concat(directives[dirName] || []); }); return result; }
Class Directives function collectClassDirectives(element) { var result = []; _.each(element[0].classList, function(cName) { var dirName = _.camelCase(cName); result = result.concat(directives[dirName] || []); }); return result; }
Applying The Directives function compileNode(element) { var directives = collectDirectives(element); directives.forEach(function(directive) { directive.compile(element); }); }
Recursing to Child Nodes function compileNode(element) { var directives = collectDirectives(element); directives.forEach(function(directive) { directive.compile(element); }); element.children().forEach(compileNode); }
1.  Compilation 2.  Linking 3.  Inheritance 4.  Isolation
Scopes • Application data + behavior • Change detection • Events
The Directive Compiler And Linker $compile Directives + DOM Compiled DOM + Linker Linker Linked DOM Compiled DOM + Scope
$compileProvider.directive('myClass', function() { return { compile: function(element) { return function link(scope, element) { element.addClass(scope.theClass); }; } }; }); Directive with a Link Function
var root = document.querySelector('#root'); var $root = angular.element(root); var linkFunction = $compile($root); $rootScope.theClass = 'decorated'; linkFunction($rootScope); Linking
The Node Link Function function compileNode(element) { var directives = collectDirectives(element); directives.forEach(function(directive) { directive.compile(element); }); element.children().forEach(compileNode); return function nodeLinkFn(scope) { }; } return function $compile(element) { return compileNode(element); };
Collect Link Functions function compileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); element.children().forEach(compileNode); return function nodeLinkFn(scope) { }; }
Apply Link Functions function compileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); element.children().forEach(compileNode); return function nodeLinkFn(scope) { linkFns.forEach(function(linkFn) { linkFn(scope, element); }); }; }
Collect Child Link Functions function compileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); var childLinkFns = element.children().map(compileNode); return function nodeLinkFn(scope) { linkFns.forEach(function(linkFn) { linkFn(scope, element); }); }; }
Apply Child Link Functions function compileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); var childLinkFns = element.children().map(compileNode); return function nodeLinkFn(scope) { childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { linkFn(scope, element); }); }; }
1.  Compilation 2.  Linking 3.  Inheritance 4.  Isolation
Scope Hierarchy $rootScope $scope $scope $scope
Scope Hierarchy vs. DOM Hierarchy <article ng-app="myApp"> <section ng-controller="..."></section> <section ng-controller="..."> <div ng-controller="..."> </div> </section> </article> $rootScope $scope $scope $scope article section section div
$compileProvider.directive('myClass', function() { return { scope: true, compile: function(element) { return function link(scope, element) { scope.counter = 0; element.on('click', function() { scope.counter++; }); }; } }; }); Directive Requests a Scope
Remember The “New Scope Directive” function compileNode(element) { var directives = collectDirectives(element), linkFns = [], newScopeDir; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); if (directive.scope) { if (newScopeDir) { throw 'No more than 1 new scope plz!'; } newScopeDir = directive; } }); // ... }
Make a new scope during linking return function nodeLinkFn(scope) { if (newScopeDir) { scope = scope.$new(); } childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { linkFn(scope, element); }); };
1.  Compilation 2.  Linking 3.  Inheritance 4.  Isolation
$rootScope $scope $scope $scope Isolate Scopes
Isolate Bindings $rootScope $scope $scope $scope expression attribute
Directive with Isolate Scope & Bindings <div click-logger="'Hello!'"></div> $compileProvider.directive('clickLogger', function() { return { scope: { message: '=clickLogger' }, compile: function(element) { return function link(scope, element) { scope.counter = 0; element.on('click', function() { console.log(scope.message); }); }; } }; });
Remember The “Iso Scope Directive” function compileNode(element) { var directives = collectDirectives(element), linkFns = [], newScopeDir, newIsoScopeDir; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); if (directive.scope) { if (newScopeDir || newIsoScopeDir) { throw 'No more than 1 new scope plz!'; } if (_.isObject(directive.scope)) { newIsoScopeDir = directive; } else { newScopeDir = directive; } } }); // ... }
Create Isolate Scope During Linking return function nodeLinkFn(scope) { var isoScope; if (newScopeDir) { scope = scope.$new(); } if (newIsoScopeDir) { isoScope = scope.$new(true); } childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { linkFn(scope, element); }); };
Remember Link Function Directives function compileNode(element) { var directives = collectDirectives(element), linkFns = [], newScopeDir, newIsoScopeDir; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFn.directive = directive; linkFns.push(linkFn); if (directive.scope) { if (newScopeDir || newIsoScopeDir) { throw 'No more than 1 new scope plz!'; } if (_.isObject(directive.scope)) { newIsoScopeDir = directive; } else { newScopeDir = directive; } } }); // ...
Apply Isolate Scope return function nodeLinkFn(scope) { var isoScope; if (newScopeDir) { scope = scope.$new(); } if (newIsoScopeDir) { isoScope = scope.$new(true); } childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { var isIso = (linkFn.directive === newIsoScopeDir); linkFn(isIso ? isoScope : scope, element); }); };
Isolate Bindings <div click-logger="'Hello!'"></div> $compileProvider.directive('clickLogger', function() { return { scope: { message: '=clickLogger' }, compile: function(element) { return function link(scope, element) { scope.counter = 0; element.on('click', function() { console.log(scope.message); }); }; } }; });
Loop Over Isolate Bindings return function nodeLinkFn(scope) { var isoScope; if (newScopeDir) { scope = scope.$new(); } if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { } ); } // ... };
Get Attribute Expression if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); } ); }
Watch & Bind The Expression if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); scope.$watch(expr, function(newValue) { isoScope[scopeName] = newValue; }); } ); }
Two-Way Data Binding $rootScope $scope $scope $scope expression attribute Two directions
Parse Expression String to Function if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); var exprFn = $parse(expr); scope.$watch(exprFn, function(newValue) { isoScope[scopeName] = newValue; }); } ); }
Refactor The Watcher if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); var exprFn = $parse(expr); scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { isoScope[scopeName] = newParentValue; } }); } ); }
Track The Parent Value _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); var exprFn = $parse(expr); var parentValue; scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { isoScope[scopeName] = newParentValue; } parentValue = newParentValue; }); } );
Check for Parent vs. Child Change var parentValue; scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { if (newParentValue !== parentValue) { isoScope[scopeName] = newParentValue; } else { } } parentValue = newParentValue; });
Propagate Change Up var parentValue; scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { if (newParentValue !== parentValue) { isoScope[scopeName] = newParentValue; } else { exprFn.assign(scope, childValue); newParentValue = childValue; } } parentValue = newParentValue; });
1.  Compilation 2.  Linking 3.  Inheritance 4.  Isolation
teropa.info -75% off list price with code ”FRAMEWORKSDAYS”

"Inside The AngularJS Directive Compiler" by Tero Parviainen

  • 1.
    Inside The AngularJS DirectiveCompiler Tero Parviainen @teropa
  • 2.
    Directives “Angular attempts tominimize the impedance mismatch between document centric HTML and what an application needs by creating new HTML constructs. Angular teaches the browser new syntax through a construct we call directives.” https://docs.angularjs.org/guide/introduction
  • 3.
  • 4.
    { priority: 0, terminal: false, template:'<div></div>', templateNamespace: 'html', replace: false, multiElement: false, transclude: false, restrict: 'A', scope: false, controller: 'MyCtrl', controllerAs: 'myCtrl', bindToController: true, require: '^parentCtrl', compile: function(el) { return { pre: function(scope, el, attrs) { }, post: function(scope, el, attrs) { } } } }
  • 5.
    src/ng/compile.js •  1307 linesof code •  84 functions •  Compilation & linking •  Attribute management •  Controllers •  Isolate bindings •  Templates & transclusion https://github.com/es-analysis/plato
  • 6.
  • 7.
    1.  Compilation 2.  Linking 3. Inheritance 4.  Isolation
  • 8.
    1.  Compilation 2.  Linking 3. Inheritance 4.  Isolation
  • 9.
  • 10.
    $compileProvider.directive('myClass', function() { return{ compile: function(element) { element.addClass('decorated'); } }; }); Directive Registration
  • 11.
  • 12.
    var root =document.querySelector('#root'); var $root = angular.element(root); $compile($root); Compilation
  • 13.
    The Compile Provider function$CompileProvider() { this.directive = function(name, factory) { }; }
  • 14.
    Directive Registration function $CompileProvider(){ var directives = {}; this.directive = function(name, factory) { directives[name] = directives[name] || []; directives[name].push(factory()); }; }
  • 15.
    Constructing $compile function $CompileProvider(){ var directives = {}; this.directive = function(name, factory) { directives[name] = directives[name] || []; directives[name].push(factory()); }; this.$get = function() { return function $compile(element) { }; }; }
  • 16.
    The compileNode helperfunction this.$get = function() { function compileNode(element) { } return function $compile(element) { compileNode(element); }; };
  • 17.
    Collecting Directives this.$get =function() { function collectDirectives(element) { } function compileNode(element) { var directives = collectDirectives(element); } return function $compile(element) { compileNode(element); }; };
  • 18.
    Three Collection Strategies this.$get= function() { function collectDirectives(element) { return collectElementDirectives(element) .concat(collectAttrDirectives(element)) .concat(collectClassDirectives(element)); } function compileNode(element) { var directives = collectDirectives(element); } return function $compile(element) { compileNode(element); }; };
  • 19.
    Element Directives function collectElementDirectives(element){ var elName = element[0].nodeName; var directiveName = _.camelCase(elName); return directives[directiveName] || []; }
  • 20.
    Attribute Directives function collectAttrDirectives(element){ var result = []; _.each(element[0].attributes, function(attr) { var dirName = _.camelCase(attr.name); result = result.concat(directives[dirName] || []); }); return result; }
  • 21.
    Class Directives function collectClassDirectives(element){ var result = []; _.each(element[0].classList, function(cName) { var dirName = _.camelCase(cName); result = result.concat(directives[dirName] || []); }); return result; }
  • 22.
    Applying The Directives functioncompileNode(element) { var directives = collectDirectives(element); directives.forEach(function(directive) { directive.compile(element); }); }
  • 23.
    Recursing to ChildNodes function compileNode(element) { var directives = collectDirectives(element); directives.forEach(function(directive) { directive.compile(element); }); element.children().forEach(compileNode); }
  • 24.
    1.  Compilation 2.  Linking 3. Inheritance 4.  Isolation
  • 25.
    Scopes • Application data +behavior • Change detection • Events
  • 26.
    The Directive CompilerAnd Linker $compile Directives + DOM Compiled DOM + Linker Linker Linked DOM Compiled DOM + Scope
  • 27.
    $compileProvider.directive('myClass', function() { return{ compile: function(element) { return function link(scope, element) { element.addClass(scope.theClass); }; } }; }); Directive with a Link Function
  • 28.
    var root =document.querySelector('#root'); var $root = angular.element(root); var linkFunction = $compile($root); $rootScope.theClass = 'decorated'; linkFunction($rootScope); Linking
  • 29.
    The Node LinkFunction function compileNode(element) { var directives = collectDirectives(element); directives.forEach(function(directive) { directive.compile(element); }); element.children().forEach(compileNode); return function nodeLinkFn(scope) { }; } return function $compile(element) { return compileNode(element); };
  • 30.
    Collect Link Functions functioncompileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); element.children().forEach(compileNode); return function nodeLinkFn(scope) { }; }
  • 31.
    Apply Link Functions functioncompileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); element.children().forEach(compileNode); return function nodeLinkFn(scope) { linkFns.forEach(function(linkFn) { linkFn(scope, element); }); }; }
  • 32.
    Collect Child LinkFunctions function compileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); var childLinkFns = element.children().map(compileNode); return function nodeLinkFn(scope) { linkFns.forEach(function(linkFn) { linkFn(scope, element); }); }; }
  • 33.
    Apply Child LinkFunctions function compileNode(element) { var directives = collectDirectives(element), linkFns = []; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); }); var childLinkFns = element.children().map(compileNode); return function nodeLinkFn(scope) { childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { linkFn(scope, element); }); }; }
  • 34.
    1.  Compilation 2.  Linking 3. Inheritance 4.  Isolation
  • 35.
  • 36.
    Scope Hierarchy vs.DOM Hierarchy <article ng-app="myApp"> <section ng-controller="..."></section> <section ng-controller="..."> <div ng-controller="..."> </div> </section> </article> $rootScope $scope $scope $scope article section section div
  • 37.
    $compileProvider.directive('myClass', function() { return{ scope: true, compile: function(element) { return function link(scope, element) { scope.counter = 0; element.on('click', function() { scope.counter++; }); }; } }; }); Directive Requests a Scope
  • 38.
    Remember The “NewScope Directive” function compileNode(element) { var directives = collectDirectives(element), linkFns = [], newScopeDir; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); if (directive.scope) { if (newScopeDir) { throw 'No more than 1 new scope plz!'; } newScopeDir = directive; } }); // ... }
  • 39.
    Make a newscope during linking return function nodeLinkFn(scope) { if (newScopeDir) { scope = scope.$new(); } childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { linkFn(scope, element); }); };
  • 40.
    1.  Compilation 2.  Linking 3. Inheritance 4.  Isolation
  • 41.
  • 42.
  • 43.
    Directive with IsolateScope & Bindings <div click-logger="'Hello!'"></div> $compileProvider.directive('clickLogger', function() { return { scope: { message: '=clickLogger' }, compile: function(element) { return function link(scope, element) { scope.counter = 0; element.on('click', function() { console.log(scope.message); }); }; } }; });
  • 44.
    Remember The “IsoScope Directive” function compileNode(element) { var directives = collectDirectives(element), linkFns = [], newScopeDir, newIsoScopeDir; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFns.push(linkFn); if (directive.scope) { if (newScopeDir || newIsoScopeDir) { throw 'No more than 1 new scope plz!'; } if (_.isObject(directive.scope)) { newIsoScopeDir = directive; } else { newScopeDir = directive; } } }); // ... }
  • 45.
    Create Isolate ScopeDuring Linking return function nodeLinkFn(scope) { var isoScope; if (newScopeDir) { scope = scope.$new(); } if (newIsoScopeDir) { isoScope = scope.$new(true); } childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { linkFn(scope, element); }); };
  • 46.
    Remember Link FunctionDirectives function compileNode(element) { var directives = collectDirectives(element), linkFns = [], newScopeDir, newIsoScopeDir; directives.forEach(function(directive) { var linkFn = directive.compile(element); linkFn.directive = directive; linkFns.push(linkFn); if (directive.scope) { if (newScopeDir || newIsoScopeDir) { throw 'No more than 1 new scope plz!'; } if (_.isObject(directive.scope)) { newIsoScopeDir = directive; } else { newScopeDir = directive; } } }); // ...
  • 47.
    Apply Isolate Scope returnfunction nodeLinkFn(scope) { var isoScope; if (newScopeDir) { scope = scope.$new(); } if (newIsoScopeDir) { isoScope = scope.$new(true); } childLinkFns.forEach(function(childLinkFn) { childLinkFn(scope); }); linkFns.forEach(function(linkFn) { var isIso = (linkFn.directive === newIsoScopeDir); linkFn(isIso ? isoScope : scope, element); }); };
  • 48.
    Isolate Bindings <div click-logger="'Hello!'"></div> $compileProvider.directive('clickLogger',function() { return { scope: { message: '=clickLogger' }, compile: function(element) { return function link(scope, element) { scope.counter = 0; element.on('click', function() { console.log(scope.message); }); }; } }; });
  • 49.
    Loop Over IsolateBindings return function nodeLinkFn(scope) { var isoScope; if (newScopeDir) { scope = scope.$new(); } if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { } ); } // ... };
  • 50.
    Get Attribute Expression if(newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); } ); }
  • 51.
    Watch & BindThe Expression if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); scope.$watch(expr, function(newValue) { isoScope[scopeName] = newValue; }); } ); }
  • 52.
    Two-Way Data Binding $rootScope $scope$scope $scope expression attribute Two directions
  • 53.
    Parse Expression Stringto Function if (newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); var exprFn = $parse(expr); scope.$watch(exprFn, function(newValue) { isoScope[scopeName] = newValue; }); } ); }
  • 54.
    Refactor The Watcher if(newIsoScopeDir) { isoScope = scope.$new(true); _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); var exprFn = $parse(expr); scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { isoScope[scopeName] = newParentValue; } }); } ); }
  • 55.
    Track The ParentValue _.forOwn( newIsoScopeDir.scope, function(spec, scopeName) { var attrName = spec.match(/^=(.*)/)[1]; var denormalized = _.kebabCase(attrName); var expr = element.attr(denormalized); var exprFn = $parse(expr); var parentValue; scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { isoScope[scopeName] = newParentValue; } parentValue = newParentValue; }); } );
  • 56.
    Check for Parentvs. Child Change var parentValue; scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { if (newParentValue !== parentValue) { isoScope[scopeName] = newParentValue; } else { } } parentValue = newParentValue; });
  • 57.
    Propagate Change Up varparentValue; scope.$watch(function() { var newParentValue = exprFn(scope); var childValue = isoScope[scopeName]; if (newParentValue !== childValue) { if (newParentValue !== parentValue) { isoScope[scopeName] = newParentValue; } else { exprFn.assign(scope, childValue); newParentValue = childValue; } } parentValue = newParentValue; });
  • 58.
    1.  Compilation 2.  Linking 3. Inheritance 4.  Isolation
  • 59.
    teropa.info -75% off listprice with code ”FRAMEWORKSDAYS”