one time bindings
» Defined with ::
» $watched until not "undefined"
» $$watcher is unbound
» Will not update upon Model changes
» One-time, not one-way
» Great for single static rendering
Slide 12
Slide 12 text
ng-Model-Options
Slide 13
Slide 13 text
ng-Model-Options
Slide 14
Slide 14 text
ng-Model-Options
// directive controller
function FooDirCtrl() {
// undo model changes
if (condition) {
this.model.$rollbackViewValue();
}
}
Slide 15
Slide 15 text
ng-Model-Options
» Fine tune how Model updates are done
» Define event types
» Add debounce to delay Model synchronisation
» e.g. { debounce: 250 } = $digest ~250ms
» $rollbackViewValue for undoing model changes
Slide 16
Slide 16 text
bindToController
// directive controller
function FooDirCtrl() {
this.bar = {};
this.doSomething = function doSomething(arg) {
this.bar.foobar = arg;
}.bind(this);
}
Slide 17
Slide 17 text
bindToController
// directive controller
function FooDirCtrl($scope) {
this.bar = {};
this.doSomething = function doSomething(arg) {
this.bar.foobar = arg;
// reference the isolate property
$scope.name = arg.prop;
}.bind(this);
}
bindToController
// directive controller
function FooDirCtrl() {
this.bar = {};
this.doSomething = function doSomething(arg) {
this.bar.foobar = arg;
// reference the isolate property
this.name = arg.prop;
}.bind(this);
}
Slide 20
Slide 20 text
bindToController
» Used with "controllerAs" (class-like)
» Binds isolate props to the Controller instance
» No $scope
» $scope remains "special use only"
» Not used for data
» Used for $watch/$on/etc
Slide 21
Slide 21 text
ngModel.$validators
// old school
function visaValidator() {
var VISA_REGEXP = /^4[0-9]{12}(?:[0-9]{3})?$/;
function link($scope, element, attrs, ngModel) {
ngModel.$parsers.unshift(function (value) {
var valid = VISA_REGEXP.test(value);
ngModel.$setValidity('visaValidator', valid);
return valid ? value : undefined;
});
}
return { require : 'ngModel', link : link };
}
angular.module('app').directive('visaValidator', visaValidator);
Slide 22
Slide 22 text
ngModel.$validators
// new school
function visaValidator() {
var VISA_REGEXP = /^4[0-9]{12}(?:[0-9]{3})?$/;
function link($scope, element, attrs, ngModel) {
ngModel.$validators.visaValidator = function (value) {
return VISA_REGEXP.test(value); // Boolean
};
}
return { require : 'ngModel', link : link };
}
angular.module('app').directive('visaValidator', visaValidator);
Slide 23
Slide 23 text
ngModel.$validators
Not a valid VISA format!
Slide 24
Slide 24 text
ngModel.$validators
» ngModel.$validators Object
» Instead of $parsers/$formatters
» Return a Boolean from the bound function
» Use with the $error Object in the View
Slide 25
Slide 25 text
ngMessage/ngMessages
Enter email:
You did not enter a field
Your email must be between 5 and 100 characters long
Slide 26
Slide 26 text
ngMessage/ngMessages
» Conditional validation
» ngModel.$error Object
» Acts like a switch case
strictDI
» Runs the application's $injector in strict mode
» Throws an error on Services using implicit
annotations
» Use ng-annotate to automate this process
Slide 31
Slide 31 text
$applyAsync with $http
function config($httpProvider) {
$httpProvider.useApplyAsync(true);
}
angular
.module('app', [])
.config(config);
More:
blog.thoughtram.io/angularjs/2015/01/14/exploring-angular-1.3-speed-up-with-applyAsync.html
Slide 32
Slide 32 text
$applyAsync with $http
» Enables $applyAsync to be used with $http
» Schedules an async $apply for batched requests
» For requests that resolve within ~10ms
» Pushes into $$asyncQueue
» Single $digest
Slide 33
Slide 33 text
Disable debug info
function config($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}
angular
.module('app', [])
.config(config);
Slide 34
Slide 34 text
Disable debug info
// content
// content
Slide 35
Slide 35 text
Disable debug info
» Disable in production for performance boosts
» Removes $scope references on elements
» Doesn't add classes to DOM nodes with binding info
» Enable in console with
angular.reloadWithDebugInfo();
Slide 36
Slide 36 text
Performance driven Angular
Slide 37
Slide 37 text
Understand what impacts
performance
before you code
$digest: $digest loop
» Triggered by $scope.$apply / built-in events
» Iterates $$watchers Array on $scope
» If model value is different from last calculated
then corresponding listener executes
» Exits loop, Angular loops again (10 max)
» Repaints DOM (View expressions updated)
Slide 49
Slide 49 text
$digest: $$watchers
» View events/bindings {{ foo }}
» Angular adds a watch to the $watch list
» Only $watched if bound in the View
» Dirty checked in the $digest loop
» Minimise use of $$watchers / avoid if possible
Slide 50
Slide 50 text
$digest: $$asyncQueue
» $evalAsync
» Runs first in $digest
» May run $digest again to flush $$asyncQueue
Slide 51
Slide 51 text
track by
Scenarios:
* Large DOM lists
* Slow DOM updates
* $digests blocking UI thread (lagging)
Slide 52
Slide 52 text
track by
{{ user.name }}
Slide 53
Slide 53 text
track by
{{ user.name }}
Slide 54
Slide 54 text
track by
» Minimal DOM repaints (only what's changed)
» Uses Object references instead of Angular hashes
Slide 55
Slide 55 text
ng-if / switch vs ng-show / hide
Slide 56
Slide 56 text
ng-if / switch vs ng-show / hide
» ng-if/switch reconstruct the DOM
» ng-if/switch for less frequent/heavier rendering
» ng-show/hide toggle "ng-hide" class
» ng-show/hide for more frequent/lighter rendering
» ng-show/hide less performant due to $$watchers
(when hidden)
Slide 57
Slide 57 text
ng-bind over {{ handlebars }}
{{ vm.username }}
Welcome to Facebook
Slide 58
Slide 58 text
ng-bind over {{ handlebars }}
» No DOM flicker (invisible bindings) with ng-bind
» Significantly faster
» ng-perf.com/2014/10/30/tip-4-ng-bind-is-faster-
than-expression-bind-and-one-time-bind
» Lesser need for ng-cloak
» Angular won't evaluate entire text content
Slide 59
Slide 59 text
$apply or $digest?
// forces a $rootScope.$digest();
$scope.$apply();
// forces a [current $scope].$digest();
$scope.$digest();
Slide 60
Slide 60 text
$apply or $digest?
» $scope certainties
» Prevent a full $rootScope.$digest() if you're
certain only child $scopes need updating
» Improve performance by not forcing a full
$rootScope.$digest
» $scope.$digest runs on current and child $scopes
» $scope.$apply triggers $rootScope.$digest call
Slide 61
Slide 61 text
$destroy unbinding
function myFunction () {
// handle element clicks
}
// bind
element.on('click', myFunction);
// unbind
$scope.$on('$destroy', function () {
element.off('click', myFunction);
});
Slide 62
Slide 62 text
$destroy unbinding
» Remove event listeners that may cause memory leaks
» DOM nodes that are destroyed
» Manually unbind by listening to $destroy
» $scope.$on events are automatically removed
Slide 63
Slide 63 text
Deep $watch vs $watchCollection
var prop = [{...},{...},{...},{...}];
$scope.$watch('prop',
function (newValue, oldValue) {
}, true);
$scope.$watchCollection('prop',
function (newValue, oldValue) {
});
Slide 64
Slide 64 text
Deep $watch vs $watchCollection
» Deep $watch uses deep Object tree comparison
(expensive)
» $watchCollection goes only one level deep
» Shallow reference of all top level items
» Try not to use either unless you have to
» Not as testable
» Signs of bad architecture
» Litter Controllers
Slide 65
Slide 65 text
avoiding DOM filters
{{ vm.date | date: 'dd-MM-yyyy' }}
Slide 66
Slide 66 text
avoiding DOM filters
function SomeCtrl($filter) {
// date passed in elsewhere
var time = 1444175093303;
// Parsed in JS before bound to the DOM
this.parsedDate = $filter('date')(time, 'dd-MM-yyyy');
}
angular
.module('app')
.controller('SomeCtrl', SomeCtrl);
Slide 67
Slide 67 text
avoiding DOM filters
{{ vm.parsedDate }}
Slide 68
Slide 68 text
avoiding DOM filters
» DOM filters run twice per $digest
» Preprocess in a Controller
» Bind parsed value to the View
Slide 69
Slide 69 text
Takeaways
» Understand the $digest loop
» Investigate the performance side of each
Directive/API you use
» Consider using JavaScript over DOM bindings where
possible ($filter etc.)
» Check Angular's GitHub repo for changelogs/
releases