AngularJS End to End
Building your First App
(basic concepts)
Setup: Template
<html ng-app='coolApp'>
…
<body>
…
<script src='angular.js'></script>
<script src='coolApp.js'></script>
</body>
</html>
Setup: JS
var myApp = angular.module('coolApp', []);
Principles
Boilerplate
D.R.Y. Structure Testability
Structure: Model-View-Controller
<div>
Observes Model
View
<span> <ul>
RAM (JS
<li> (DOM)
Objects)
Notifies Manages
Controller
(JS Classes)
Model:
{ "name": "Misko" }
View:
<p ng-controller='PersonController as person'>
Hi, {{person.model.name}}
</p>
Controller:
myApp.controller('PersonController', function () {
this.model = { name: 'Misko' };
});
Model:
{ "name": "Misko" }
View:
<p ng-controller='PersonController as person'>
Hi, {{person.model.name}}
</p>
Controller:
myApp.controller('PersonController', function (s) {
this.model = s.getPerson();
});
Feature: Data Binding
hello.js
this.greeting = 'Hola!';
hello.html
{{ greeter.greeting }} // 1-way
<input ng-model='greeter.greeting'> // 2-way
Note: Binding for initial page load
Markup fine except for first page
<p>{{someObj.something}}</p>
Use ng-cloak to avoid unstyled content on load
<p ng-cloak>{{someObj.something}}</p>
Attribute form also avoid unstyled content on load
<p ng-bind='someObj.something'></p>
Feature: Directives
<div ng-repeat='item in cart.items'>
<span ng-bind='item.name'></span>
<button ng-click='cart.delete($index)'>
Delete
</button>
</div>
Note: Directive validation
<div data-ng-repeat='item in cart.items'>
<span data-ng-bind='item.name'></span>
<button data-ng-click='cart.delete($index)'>
Delete
</button>
</div>
Feature: Dependency Injection
Definition
function MyFoo() { … };
module.service('foo', MyFoo);
Usage
function MyBar(foo) {
Foo instanceof MyFoo; // true!
}
Preferred usage
myApp.service('foo', function() {
...
});
Principle: Eliminate boilerplate
Dependency Injection
POJO Models
No DOM in most code paths
View Templates
Directives
Markup {{ }}
Form Validation
Filters
Filters
<p>{{ invoice.total | currency }}</p>
Filter example: Reverse
myApp.filter('reverse', function() {
return function(input) {
var out = '';
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
}
return out;
}
});
Back to Controllers
Thus far:
● Expose model to view
● Expose functions to handle user interaction
● Update model to change the view
But also:
● Instantiated once per use in your template
● Can request $scope
$scope
You can request $scope in controllers
myApp.controller('GreetCtrl', function ($scope) {
$scope.greeter = { greeting: 'Misko'};
});
Use in template
<p ng-controller='GreetCtrl'>{{greeter.greeting}}</p>
$scope
Context for expression evaluation
Hierarchical, mirroring your DOM structure
Prototypal inheritance = global read, local write
$scope
$watch(expression, callback): Executes a callback
whenever a given expression changes
$apply() : synchronize data binding from outside
Angular's event loop
Expressions
{{ The stuff inside your markup }}
ng-bind='and inside directives'
Like JavaScript, but eval'd against your $scope
No complex logic like loops or throws
Though terinary operator is supported
Expression Examples
person.name
shoppingCart.total()
cart.total() - user.discountRate
Talking to servers
$http: HTTP server communication
$resource: RESTful server communication
Notable other options:
● Restangular
● AngularFire
● BreezeJS
$http
this.someData = $http.get('/someUrl');
$http
$http.get('/someUrl').
success(function(data, status, headers, config) {
// called asynchronously
}).
error(function(data, status, headers, config) {
// called if an error occurs, also async
});
Promises
$http.post('/user/', user).then(
function() {
alert('Successfully created user!');
}, function() {
alert('Error creating user...');
}
);
Routes
Header
Left Nav
Content View
Routes
<ng-view> directive as content placeholder
$routeProvider to configure:
● Controller
● Template
● URL
Routes
myApp.config(function($routeProvider) {
$routeProvider.when('/inbox', {
templateUrl: 'inbox.html',
controller: 'InboxCtrl',
controllerAs: 'inbox'
});
});
Resources
Tools UI Components Libraries Books
Batarang Bootstrap
Learning Resources
docs.angularjs.org egghead.io thinkster.io pluralsight.com
/tutorial
Questions?
Internals of AngularJS
(How it all works)
Data Binding
Goal: Separate code from HTML
Data Binding
<div>
Model Drives <span> <ul>
<li>
<li>
<li>
<div>
Model Depends on <span> <ul>
<li>
<li>
<li>
Data Binding: Requirements
• Model is a Plain-Old-JavaScript-Object
• Detect value changes
• Easily describe value in the model
• Encapsulate DOM access
Scope
Goal: Provide context for expressions
Scope
User = function () {
this.fullName = 'Miško Hevery';
this.username = 'mhevery';
}
scope = {};
scope.user = new User();
{{user.fullName}}
{{user.username}}
Scope
{{name}}
<ul>
<li ng-repeat="name in names">
{{name}}
</li>
</ul>
Scope
parent = {};
child = inherit(parent);
parent.name = 'parent';
expect(child.name).toEqual('parent');
child.name = 'child');
expect(child.name).toEqual('child');
expect(parent.name).toEqual('parent');
Scope
function inherit(parent) {
function C() {}
C.prototype = parent;
return new C();
}
parent = {};
child = inherit(parent);
expect(child.__proto__).toBe(parent);
Scope
{{name}}
<ul>
<li ng-repeat="name in names">
{{name}}
</li>
</ul>
Watchers
Goal: Notify of model changes
Watchers
{{name}}
scope.name = 'Miško Hevery';
scope.$watch('name', function(value) {
element.text(value);
});
Digest Cycle
Goal: Non-intrusive change detection
Digest Cycle
scope.$watch('name', reactionFn);
var lastValue;
function dirtyCheck() {
var value = scope.name;
if (lastValue != value)
reactionFn(value);
lastValue = value;
}
Digest Cycle
scope.$watch('count', function(value) {
alert(value);
});
scope.$watch('name', function(value) {
scope.count++;
});
Digest Cycle
Goal: Knowing when to digest
Digest Cycle
Digest Cycle: Sync vs Async
var result1 = cacheApi(function() {
result1.doSomething();
});
• API should not mix sync/async
• Need a way to simulate async callbacks
• setTimeout(0) is problematic
Digest Cycle: Sync vs Async
StackFrame
#3 asyncApi()
#2 yourApplicationBehavior()
#1 ng.$apply()
#0 nativeCode(event)
Expressions
Goal: Code of least surprise
Expressions
{{user.firstName}}
eval('with(scope){return
user.firstName;}');
if (scope.user) {
return scope.user.firstName;
}
Expressions
• eval() can not be the solution
• silent dereference
• executes in global context
• CSP
• Security
Expressions
• Let's think out of the box
• Let's implement JavaScript in JavaScript
• Full lexer and parser implies full control over:
• Syntax
• Semantics
Directive
Goal: Express intent in HTML
Directive
<ul>
<li ng-repeat="user in users">
{{user.name}}
</li>
</ul>
Directive
scope.$watch('name', function(value) {
element.text(value);
});
<span ng-bind="name"></span>
Compiler
Goal: extend HTML syntax
(teach browser new tricks)
Compiler
<div ng-controller="MyCtrl as c">
<div ng-repeat="name in c.names">
{{name}}</div>
</div>
• Traverse the DOM => locate directives, remember location
• Template: Easily apply directives to DOM clone
Dependency Injection
Goal: Get rid of setup code
Dependency Injection
function Door() {}
function House(door) {}
function App(house) {}
// implied
app = new App(new House(new Door)));
Dependency Injection
How do you know what to inject?
Dependency Injection
function Door() {}
function House(door) {}
House.$inject = ['door'];
function App(house) {}
App.$inject = ['house'];
Dependency Injection
describe('something', function() {
function test($rootScope){}
test.$inject = ['$rootScope'];
it('should ...', test);
});
Dependency Injection
function House(door) {}
House.$inject =
creazyRegExp(House.toString());
Dependency Injection
function Door(){}
function House(door) {}
injector = new Injector(...);
house = injector.instantiate(House);
Dependency Injection
function instantiate(Type) {
var args = Type.$inject.map(instantiate);
return new Type.call(args);
}
Dependency Injection
Add Caching, Modules and
some syntactic sugar and
you have full DI system
Routing
Goal: Preserve the web paradigm
Routing
• Deep linking
• Links work like links
• Support HTML5 pushstate
• URL rewriting
Routing
<a href="/chapter/{{id}}">
http://server.com/chapter/123
http://server.com/#!/chapter/123
• Server cooperation needed
REST
Goal: Easy server communication
REST
• Make common case simple
• Take advantage of data-binding
• Future / Promises
REST
Chapter = $resource('/chapter/:chapterId');
var chapters = Chapter.query();
<div ng-repeat="chapter in chapters">
{{chapter.name}}
</div>
Questions?
The New Stuff
(animations, track by, controller as, security,
error messages)
Brian Ford and Igor Minar
Animations in Angular
Animations are a natural extension
of CSS3 animations and
AngularJS directives
Animations in Angular
Angular automatically coordinates DOM
operations, so you can focus on the
visuals of the animation.
Animations
// HTML
<div ng-repeat="item in list" class="item">{{item}}</div>
//CSS
.item.ng-enter, .item.ng-move { .item.ng-leave {
transition:0.5s linear all; animation: 0.5s my_animation;
opacity: 0; }
}
@keyframes my_animation {
.item.ng-enter.ng-enter-active, from { opacity:1; }
.item.ng-move.ng-move-active { to { opacity:0; }
opacity: 1; }
}
Animation Hooks
http://docs.angularjs.org/guide/animations
Animation in Action
Example App: https://github.com/matsko/ngconf-animations-demo
Animation (& Touch) in Action
Example: Sidebar
● ngIf
● ngSwipeLeft/Right
Animation in Action
Example: Slideshow
● ngView
Animation in Action
Example: Notifications
● ngRepeat
Animation Best Practices
With great power comes great responsibility
● Be tasteful
○ use sparingly
● They should enhance UX
○ same functionality, but better affordances
● Keep them fast
○ http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/
Animation and track by (ng-repeat)
• ng-repeat is stable
• Database data needs to be refreshed
<div ng-repeater="o in objs track by o.id">
...
</div>
"Controller As"
Before:
<div ng-controller="UserController">
<p>{{name}}</p>
<div ng-if="someCondition">
<input ng-model="name">
</div>
</div>
"Controller As"
Unambiguously bind to models:
<div ng-controller="UserController as
user">
<p>{{user.name}}</p>
<div ng-if="someCondition">
<input ng-model="user.name">
</div>
</div>
Security Enhancements
● Template origin verification
● Expression sandbox hardening
● Better Content Security Policy support
● SCE
SCE (strict contextual auto-escaping)
<input ng-model="userHtml">
<div ng-bind-html="userHtml">
SCE (strict contextual auto-escaping)
var htmlSrc = '<div>...</div>';
var htmlWrapper = $sce.trustAsHtml(htmlSrc);
var htmlDst = $sce.getTrustedHtml(htmlWrapper);
Improved Error Messages
Before:
Uncaught Error: No module: ngResource
After:
Uncaught Error: [di-nomod] Module 'ngResource' is not
available! You either misspelled the module name or forgot to
load it.
http://docs.angularjs.org/error/di-nomod?a=ngResource
In production:
Uncaught Error: [di-nomod] ngResource
http://docs.angularjs.org/error/di-nomod?a=ngResource
minErr - Improved Error Messages
[$rootScope:infdig] http://...?p0=10&...
Questions?