advanced angularjs
DESCRIPTION
Slides for the one day Advanced AngularJS class.TRANSCRIPT
AngularJS Deep Dive13 September 2014 Troy Miles
Troy Miles
Over 30 years of programming experience
Blog: http://therockncoder.blogspot.com/
Twitter: @therockncoder
Email: [email protected]
GitHub: https://github.com/Rockncoder
AgendaHow AngularJS works
$scope
Routing
Filters
Unit testing
Directives
Providers & services
$http
$q
Directives Deep Dive
Summary
What We Won’t Cover
Modern JavaScript Programming
Design Patterns
Other Frameworks
How AngularJS Works
AngularJSCreated by Miško Hevery and Adam Abrons in 2009
JavaScript MVC
106 kb production version (minimized, not gzipped)
Declarative programming for UI
Imperative programming for business logic
AngularJS <!DOCTYPE html><html ng-app="moduleName"> <head lang="en"> <meta charset="UTF-8"> <title>NG-Skeleton</title></head> <body><div ng-controller="aController"> </div><div ng-controller="anotherController"> </div><!-- if using jQuery it goes here --> <script src="../libs/angular.min.js"></script><!-- other libs and script files --> </body> </html>
ng-app
Declares the boundary of an Angular app
The first ng-app found will be auto-bootstrapped
ng-app can optionally name a module
Can encompass the entire page <html ng-app>
Or only part of it, <div ng-app>
What the Browser Does?
Loads the HTML
Parses it into a Document Object Model or DOM
The angular.js script is loaded and parse
Angular waits for the DOMContentLoaded event
AngularJS’ bootstrap code executed
DOMContentLoaded vs load event
The load event fires once everything has loaded
The DOMContentLoaded event fires once the DOM has been created
DOMContentLoaded doesn’t wait for CSS, images, or iFrames to load
DOMContentLoaded fires well before load
AngularJS’ Bootstrap
Bootstrap looks for the ng-app directive
There can only be one of these
The module specification is optional
The module specification tells the $injector service which defined module to load
The $injector serviceCreates $rootscope
The mother of all scopes
Linked to the DOM
Creates the $compile service (like a shepherd)
Walks the DOM looking for directives
How ng sees directives no matter how defined
AngularJS Key FeaturesModel View Controller (MVC)
Data Binding
HTML Templates
Dependency Injection
Deep Linking
Directives
Model View Controller
Uses MVC or MVVM or MV* depends on who you ask
The goal is clear separation of concerns
The model is only concerned with data
The view presents the data to the user
The controller applies the business logic
Data Binding
In Angular, binding is built into the framework
Replaces text content of HTML element with the value of the expression
{{ expression }}
<ANY ng-bind=“expression”>…</ANY>
<ANY class=“ng-bind: expression;”>…</ANY>
HTML Templates
Many JavaScript MVC Frameworks use a client-side templating engine
AngularJS doesn’t
Angular uses HTML as its templating engine
No extra markup, no extra libraries
Dependency InjectionA software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle
Allows a dependency to be passed to an object
Allows code to clearly state dependencies
Leads to code which is easier to debug and test
Makes it possible to minimize apps without breaking them
Dependency InjectionThe DI pattern in AngularJS looks funky due to JavaScript’s shortcomings
The pattern is name of module, dot, name of provider, name of object, an array with the list of dependencies in strings and a function as the last item in the array
tempApp.controller('CurrentCtrl', ['$scope', function ($scope) { $scope.temp = 17; }]);
Deep Linking
Deep Linking enables a website to restore state from a URL
URL and bookmarks are key part of the web
Early Single Page Apps lacked deep linking
They broke SEO and user expectation
Directives
“The coolest feature of AngularJS” - Misko Hervey
Attach behavior to DOM elements
Can nearly turn HTML into a DSL
$scope
InheritanceClassical Inheritance
Most object oriented languages use classes as their inheritance mechanism
Prototypical Inheritance
Objects inherit from other objects, no need for classes
Demo child inheriting from parent
Once a property is added, everyone on the inheritance chain gets it, regardless of when created
Scope Inheritance
Scopes can be nested to limit access to app properties
Provides access to shared model properties
Nested scopes are either "child scopes" or "isolate scopes"
Child scope inherits prototypically
An isolate scope does not
Plain Old JavaScript ObjectsAngular uses dirty checking to keep the model and view in sync
Dirty checking runs equality checks over the data the view depends, it is a brute force method
Watch expressions are run every time data could change
Watch expression should be fast and idempotent
$watch()
Watches for model mutations
$watch(watched expression/function, handler, …)
Watch expression must be fast and idempotent
$apply()
Called when you are transitioning from non-angular world into angular world
calls $digest
$digest()
A digest is just plain old dirty checking
It works on all browsers and is totally predictable
Digest Loop
Possibility of an endless loop
Will only go 10 deep before exception is thrown
NOT like a game loop
$parse()
Evaluates expressions, safely
Bridges gap between the DOM and AngularJS
Used by AngularJS to interpolate values
Module
A collection of configuration and run blocks which get applied to the app during bootstrap
Most apps will have one module
Most 3rd party libraries will come with their own
You inject dependent modules into yours
Configuration Blocks
Get executed during the provider registration and configuration phase
Only providers and constants can be injected into configuration blocks
Shortcut methods available for some common configuration blocks
Configuration Shortcuts
value(‘constantName’, 123)
factory(‘factoryName’, function(){return 123;})
directive(‘directiveName’, …)
filter(‘filterName’, …)
Run Blocks
Closest thing Angular has to a main method
Executed after services have been configured
Typically contains code which is hard to unit test
Order of Execution
Configuration blocks
Run blocks
Directive compile functions
Controllers
Directive link functions
$timeout
guaranteed to fire after current digest loop
If you use a setTimeOut, you will also need to do $apply
$timeout does this for you
Lab - $scope
Filters
Understanding FiltersUsed to format data displayed to user
Strictly front-end, doesn’t change model data
Accessible using declarative or imperative syntax
{{ expression [| filter_name[:parameter_value] ... ] }}
$scope.originalText = 'hello';$scope.filteredText = $filter('uppercase')($scope.originalText);
A tour of built-in filterscurrency
date
json
lowercase
uppercase
number
filter
limitTo
orderBy
Lab - Using Filters
Using Filters in Controllers
The filter must be injected into the controller
Use the mangled name, <filterName>Filter
Filters can also be used with services and directives
Building custom filterstempApp.filter('minimum', [function () { return function (arrTemp, minimum) { var filteredArray = []; var min = minimum ? minimum : 15; angular.forEach(arrTemp, function (value, key) { if (value.temp >= min) filteredArray.push(value); }); return filteredArray; }; }]);
Lab - Custom Filters
Unit Testing
Jasmine
The default unit tester for angularjs
Others will also work
Behavior driven approach
Describe - test suite
Describe is a global jasmine function
Two params
string - name of the test suite
function - implementation of the suite
Can be nested
it - specs
it is a global jasmine function
Looks like describe
A spec contains one or more expections
If all expectations true, it is a passing spec
If any expectation fails, it is a failing spec
Expectations
Expect function
One param
The actual
Chained to a Matcher function
Matchers
Take the output of the expect function and compare it to something
Implement a boolean compare between actual value and expected
Reports to Jasmine if the expectation is true or false
Any matcher can be negated with a not before it
Some matchers
toBe - compares using ===
toEqual - works for literal variables and objects
toMatch - for regular expressions
toBeDefined - compares against 'undefined'
toBeUndefined - also compares against ‘undefined'
Some matchers (CONTINUE)
toBeNull - compares against null
toBeTruthy - truthy boolean casting
toBeFalsy - falsy boolean casting
toContain - finds an item in array
Some matchers (CONTINUE)
toBeLessThan
toBeGreaterThan
toBeCloseTo - precision math comparison
toThrow - should throw an exception
beforeEach / afterEachAre setup and teardown functions
called before and after each spec it
this
beforeEach, it, and afterEach share the same this
it is cleared before call spec call
any beforeEach not included in a describe block is executed before any Jasmine test
can use this to add custom matchers
Disabling suites and specs
prepend an 'x' before describe or it
specs inside a disabled suite are not ran
Lab - Unit Testing
Directives
Directives
Introduction to Directives
jQuery integration
Using a jQuery UI Widget
Directives
Markers on a DOM element that attach a behavior to it
Can be an attribute, element name, comment, or CSS
The HTML compiler traverses the DOM at bootstrap and matches directives to DOM elements
Directives Names<div timePicker></div>
<div time-picker></div>
<div time:picker></div>
<div time_picker></div>
<div x-time-picker></div>
<div data-time-picker></div>
Directive Location
Tag name: <timePicker></timePicker>
Attribute: <div data-rnc-time-picker></div>
Class: <div class=“time-picker;”></div>
Comment: <!— directive:time-picker —>
Built-in Directives
ng-app
ng-bind
ng-controller
ng-href
ng-readonly
ng-repeat
ng-src
ng-submit
ng-transclude
ng-view
jQuery Integration
AngularJS includes a mini version of jQuery called jqLite
It is perfectly compatible with the full version of jQuery
jQuery must be loaded before Angular or it won’t see it
Using a jQuery Widgetapp.directive('timePicker', function () { var today = new Date(new Date().toDateString()); return { require: '?ngModel', link: function ($scope, $element, $attrs, ngModel) { var initialized = false; ngModel = ngModel || { "$setViewValue": angular.noop }; // where is the missing time value? setTimeout(function () { initialized = $element.timepicker() .on('changeTime', function (ev, ui) { var sec = $element.timepicker('getSecondsFromMidnight'); ngModel.$setViewValue(sec * 1000); console.log("sec = " + sec); }); }); ngModel.$render = function (val) { if (!initialized) { //If $render gets called before our timepicker plugin is ready, just return return; } $element.timepicker('setTime', new Date(today.getTime() + val)); } } }});
Lab - jQuery UI Widget
ng-bind vs ng-modelng-bind is one way data binding, aka output
ng-bind renders a property on scope
ng-bind has a shortcut, {{expression}}
ng-bind is preferred over shortcut
ng-model is for two-way data binding
ng-model is intended for form elements
Providers
What are providers?
Types of providers
Services
Factories
Providers
What are providers?
Objects that are instantiated and wired together automatically by the injector service
The injector creates two kinds of objects:
services - defined by the developer
specialized objects - Angular framework pieces, controllers, directives, filters, or animations
Types of providersConstants
Value
Decorator
Provider
Service
Factory
Services
Substitutable objects that are wired together using DI
Used to organize and share code across app
Only instantiated when an app component depends on it
Singletons
Built-in services always start with “$”
Factories
Introduction to shared components
Dependency injection deep dive
Building custom factories & services
Persisting data to a Web API service
Routing
Server-side Routing vs Client-side Routing
Traditionally changing the URL triggers server side request
Angular watches $location.url() and tries to map it to a route definition
ngRoute
Provides routing and deep linking services and directives
Must include angular-route.js in HTML, after angular.js
Must mark ngRoute as a dependent module
Use ng-view indicate where partials will be displayed
Pushstate URLs vs hash based URLs
By default Angular uses hash “#” based URLs
It can use HTML5 push state, with fallback
$locationProvider.html5Mode(true);
Lab - Routing
$http
$http
Facilitates communication with a remote HTTP service via the XMLHttpRequest object or JSONP
Based on deferred/promise API
Requires two parameters:
method - the HTTP method to call
url - the location of the HTTP service
JSON Test
A free service for testing JSON based application
http://www.jsontest.com
Echo JSON returns a customized JSON object
Lab - $http
Directives Deep Dive
jQuery vs Directive<body> <div id="myElement" my-directive></div> </body> $(document).ready(function(){ $('#myElement').myPlugin({pluginOptions: options}); });
Directives are …
The heart of AngularJS
Declarative
Data driven
Conversational
.directive
Is a shortcut to the same method on compiler provider
Same pattern used in controllers, services, and filters
Not actually a a directive definition but a factory
Parameters
$scope - the scope for this instance of the directive
$element - the jQuery wrapped element
$attrs - any attributes attached to the element
Return value
a configuration object
a function
Returns a functionangular.module('myApp.directives', []) // this is a factory method which actually creates .directive('myAwesomeDirective', ['api', function () { // one time initializations return function ($scope, $element, $attrs) { // directive work goes here }; }]);
Returns an objectangular.module('myApp.directives', []) // this is a factory method which actually creates .directive('myAwesomeDirective', ['api', function (api) { // one time initializations // return a configuration object return { restrict: ‘A’, priority: 10, terminal: false, template: '<div<h3>{{title}}</h3></div>', templateUrl: 'myDirective.html', replace: true, compile: function (element, attributes, transclude) {}, link: function($scope, $element, $attrs){}, scope: true, controller: function($scope, $element, $attrs){}, require: 'ngModel', transclude: true }; }]);
Configuration objectrestrict
priority
terminal
template/templateUrl
replace
compile
link
scope
controller
require
transclude
restrictDefines use of directive
Styles (EACM)
E - element
A - attribute, the most common
C - Class - RARELY USED
M - Comment - RARELY USED
E - element
Very semantically accurate
Looks cool
Internet Explorer issue
will read in as div
to fix, in headdocument.createElement(‘my directive')
A - attribute
The default if restrict not defined
Protects from IE troubles
Provides some semantic meaning
Best practice
priority
Specifies in what order the directives should be executed
There may be multiple directives on the same node
default is 0
terminal
Related to priority
terminal dictates whether or not directive execution should stop after the priority level
default is false
template/templateUrl
Both template properties function mostly the same
For templateUrl, compile/link process will be suspended until the template is loaded
Can contain other directives nested within them
replace
Whether the whole element should be replaced with the template, or just the element's inner HTML
If replace - must have only one root node
default is false
compile/link
compile - tasks requiring restructuring of the DOM
link - attaches scope to the compiled element
Could replace the configuration object with a link function
scope
default - null - same scope as attached object
true - new scope plus inherits from parents (prototypal inheritance)
{} - isolate scope object
controller
can store many of the properties or methods that you might normally attach to scope
if attached to the controller itself, they can be shared with other directives
requireTells Angular to grab the instance of one directive's controller and make it available to another directive
?ngModel, means that requirement is optional
^ngModel, traverse up from the element node through the DOM tree <div ng-model="data.property"> <input autocomplete-input /> </div>
transclude
provides ability to have isolate scope
but still have access to parent scope's properties
jQuery UI Widget Walk-thru
Project Organization
Recommend seeding with Yeoman
Yeoman will create the app scaffolding
It also includes both Jasmine for unit tests
And Karma as a test runner
Getting Yeoman running on Windows is slightly challenging
Yeoman
Scaffolding for modern web apps
Yeoman is a node package module, so node is required
It is free and open source
http://yeoman.io/
Promises / $q
Why promises?$.get('api/gizmo/42', function(gizmo) { console.log(gizmo); // or whatever});
But quick can turn into…$.get('api/gizmo/42', function(gizmo) { $.get('api/foobars/' + gizmo, function(foobar) { $.get('api/barbaz/' + foobar, function(bazbar) { doSomethingWith(gizmo, foobar, bazbar); }, errorCallback); }, errorCallback); }, errorCallback);
What is a promise?
A promise is an object that represents the return value or the thrown exception that the function may eventually provide
A promise can also be used as a proxy for a remote object to overcome latency
Resolution of a promise is always asynchronous
What is $q?
Inspired by the Kris Kowal's Q library
Q has more features than $q
Q is also bigger
$q is bound to the $rootScope for faster propagation
The promise pattern
A promise has two components
Deferreds - represents a unit of work
Promises - represents data for the Deferreds
The deferred API
resolve()
reject()
notify()
The promise API
then
catch
finally
Lab - $q
Best PracticesKeep watch expression fast and idempotent
Write unit tests
Keep your code organized
Keep controllers simple
Business logic belongs to models, not controllers
No DOM code in controllers, use directives
SummaryHow AngularJS works
$scope
Routing
Filters
Unit testing
Directives
Providers & services
$http
$q
Directives Deep Dive
Summary
BooksGreen, Brad & Seshadri, Shyam
AngularJS
O'Reilly Media
2013
Vanston, Alex
AngularJS Directives
Packt Publishing
2013
Books…Ford, Brian & Ruebbelke, Lukas
AngularJS in Action
Manning Publications
2014
Knol, Alex
Dependency Injection with AngularJS
Packt Publishing
2013
Books…Hahn, Evan
JavaScript Testing with Jasmine
O'Reilly
2013
Kozlowski, Pawel & Darwin, Peter Bacon
Mastering Web Application Development with AngularJS
Packt Publishing
2013
Angular Linkshttps://angularjs.org/
https://github.com/angular-ui
http://www.alexrothenberg.com/2013/02/11/the-magic-behind-angularjs-dependency-injection.html
http://arthur.gonigberg.com/2013/06/29/angularjs-role-based-auth/
http://blog.xebia.com/2013/09/01/differences-between-providers-in-angularjs/
Testing Links
http://tryjasmine.com/
http://www.summa-tech.com/blog/2014/05/19/a-beginners-guide-to-angular-unit-tests
http://jasmine.github.io/2.0/introduction.html
http://andyshora.com/unit-testing-best-practices-angularjs.html
Misc. Links
http://ablogaboutcode.com/2011/06/14/how-javascript-loading-works-domcontentloaded-and-onload/
http://garann.github.io/template-chooser/