Вы находитесь на странице: 1из 53

Crash Course

Keith Bloomfield
Lead Developer
dev9

Crash Course
Keith Bloomfield
Lead Developer
dev9

Show of Hands
Front-end developers?
Back-end developers?
Other?
Using AngularJs at work/home?
New to AngularJs?

Crash Course
Agenda
Overview
Data Binding, Expressions, Scopes,
Ng Directives
Modules, Dependency Injection
Controllers, Services, Factories, Directives
Unit Testing
End-to-End (e2e) Testing
Continuous Integration
Real World Experiences
Tool Support
Resources for Learning
Q&A

Overview

AngularJs is a Spa framework

Manicure/Pedicure

Body/Facial Treatment

Massage

A lot has changed on


the front-end in the last
few years!

Overview

AngularJs is a Spa framework

Single-Page Application (dynamic views)

Recommends client-side Model-View-Controller (Mvc) pattern

Overview

AngularJs is a Spa framework

Single-Page Application (dynamic views)

Recommends client-side Model-View-Controller (Mvc) pattern

Don't
know about
each other.

Overview

AngularJs is a Spa framework

Single-Page Application (dynamic views)

Recommends client-side Model-View-Controller (Mvc) pattern

Overview

AngularJs is a Spa framework

Single-Page Application (dynamic views)

Recommends client-side Model-View-Controller (Mvc) pattern

Implications:
Routing
History
Caching
Packaging
Deployment
Automation
Data Binding
Object Modeling
Timing/Dispatch
Templating
Testing
Storage
...

AngularJs

Overview

AngularJs is a Spa framework

Single-Page Application (dynamic views)

Recommends client-side Model-View-Controller (Mvc) pattern

What becomes
of this?

Overview

AngularJs is a Spa framework

Single-Page Application (dynamic views)

Recommends client-side Model-View-Controller (Mvc) pattern

Model-View-Whatever (Mvw)

The View

foo.html

Templates/Partials
Html content swapped into/out of the single page
d.html
my-app.html
my-header.html
my-cart.html
a2.html
a2.html

a1.html a3.html
b1.html b2.html
b3.html

c2.html

bn.html
c1.html

my-news.html

my-tools.html
my-mail.html

Data Binding, Expressions and


Scopes

Extending Html's vocabulary: Directives


Extend Html as markers on the Dom
Can be used as an attribute, element, class name, comment
Tell AngularJs' compiler ($compile) to attach specific behavior to that
Dom element or even transform that element and its children.

<!DOCTYPE html>
<html ng-app>
<head>
<title>echo</title>
</head>
<body>
<div class="container">

auto-bootstraps the
AngularJs application/
The compilation is a process
designates the root
Angular (ng)
of walking the DOM tree and
element.
binds
input, select, textarea
to a DOM elements to
Directives
matching
property on the scope using
ng-model
directives.
(NgModelController), and more. (links scope to template)

Name: <input type="text" ng-model="echo"/>


<br/>
{{ echo }}
</div>
<script src="scripts/angular.min.js"></script>
</body>
</html>

Data Binding
Evaluated
against
Expression
the scope.

Data Binding, Expressions and


Scopes

Extending Html's vocabulary: Directives


Extend Html as markers on the Dom
Can be used as an attribute, element, class name, comment
Tell AngularJs' compiler ($compile) to attach specific behavior to that
Dom element or even transform that element and its children.

Data Binding, Expressions and


Scopes

Extending Html's vocabulary: Directives

The only place where an AngularJs application


touches the Dom is within directives.
This is good as artifacts that access the Dom
are difficult to test.

More about directives later, but first...

Data Binding, Expressions and


Scopes

$scope

Acts as the glue between the view and the controller

Refers to the application model

Arranged hierarchically to mimic the Dom

Can inherit from parent scope

Can watch expressions and propagate events

Data Binding, Expressions and


Scopes

$scope

$rootScope: Defined at the top level of the application (where the


ng-app directive is applied)
Lookup proceeds up the scope chain when a variable is not found
in the local scope.
Relying on scope is risky because the Dom layout is often changed.
- Isolating scope protects against changes to the Dom hierarchy.

Data Binding, Expressions and


Scopes

$scope

$watch: Observe model mutations


$apply: Propagate model changes through the system into the
view from outside the Angular realm (controllers, services, ng event
handlers). Triggers $digest, which can be used directly in unit tests.
Also available: $watchCollection, $destroy, $eval, $evalAsync, $on,
$emit, $broadcast

Dependency Injection
Module

Reusable container for different features of


your app

Dependency Injection
Module

Has two phases:

config: setup providers and constants


run: after injector is created, kickstart the app

angular.module('myModule', ['moduleDependency1', 'moduleDependency2',....])


.config(function(injectable1, injectable2,...) { // provider-injector
// Use as many of these as you want.
// Can only inject Providers (not instances)
})
.run(function(injectable1, injectable2,....) { // instance-injector
// Use as many of these as you want.
// Can only inject instances (not Providers) and constants
});

Dependency Injection
Module

Features can be bundled and injected in/as


modules

Dependency Injection

$provide
- Object in module auto

auto
- Implicit module which gets automatically added to each $injector

$injector
- used to retrieve object instances as defined by provider, instantiate types,
invoke methods, and load modules.

Dependency Injection

$provide
- Helper methods (also exposed on module):

provider(provider) - registers a service provider with the $injector


constant(obj) - registers a value/object that can be accessed by providers and
services.
value(obj) - registers a value/object that can only be accessed by services, not
providers.
factory(fn) - registers a service factory function, fn, that will be wrapped in a
service provider object, whose $get property will contain the given factory
function.
service(class) - registers a constructor function, class that will be wrapped in a
service provider object, whose $get property will instantiate a new object using
the given constructor function.

Worth investigating: $provide.decorator() // $provide only


https://egghead.io/lessons/angularjs-provide-decorator

Dependency Injection

$provide
- Helper methods (also exposed on module):

provider(provider) - registers a service provider with the $injector


constant(obj) - registers a value/object that can be accessed by providers and
services.
value(obj) - registers a value/object that can only be accessed by services, not
providers.
Added to the Dom using ng-controller
factory(fn)
- registers a service factory function, fn, that will be wrapped in a
service provider object, whose $get property will contain the given factory
function. The ng-controller directive asks the injector
service(class)
- registersan
a constructor
function,
classcontroller
that will be wrapped
to create
instance
of the
and initsa
service provider object, whose $get property will instantiate a new object using
dependencies.
the given constructor
function.

Where is the Controller?

The controller itself never knows about the


injector.
Loose coupling is maintained.

Dependency Injection

Dependencies are looked up by name for a


provider that satisfies the argument.
app.controller(AController, function($scope, $http){
$scope.userName = 'Walt';
$scope.foo = $http.get('http://foo.com');
});

Minified:
a.controller(AController, function(b, c){
b.userName = 'Walt';
b.foo = c.get('http://foo.com');
});
OH NO!

Order doesn't matter


$scope found in
ng module
- $rootScopeProvider
- $rootScope.Scope
$http service found
ng module
- $http

Dependency Injection

Longer version preserves dependency by


treating it as an argument:
app.controller(AController, ['$scope', '$http', function($scope){
$scope.userName = 'Walt';
$scope.foo = $http.get('http://foo.com');
}]);

Minified:

Order matters

a.controller(AController, ['$scope', '$http', function(b, c){


b.userName = 'Walt';
b.foo = c.get('http://foo.com');
}]);
Passed in as arguments
to the function

Dependency Injection

Minification Pain Relief: ngmin


https://github.com/btford/ngmin
It turns this
angular.module('whatever').controller('MyCtrl', function ($scope, $http) { ... });

into this
angular.module('whatever').controller('MyCtrl', ['$scope', '$http', function ($scope, $http) { ... }]);

Controllers

Used to augment the Angular scope

Attached to the Dom via the ng-controller directive

Set up the initial state of the $scope object

Add behavior to the $scope object

app.controller('HomeController', function($scope){
$scope.pizzas = [
{
name: "Meat Lover's",
ingredients: ['Sausage','Pepperoni','Bacon','Olives'],
price: 6
},
{
name: 'Hawaiian',
ingredients: ['Canadian bacon','Pineapple'],
price: 5
},
];
});

<html ng-app="pizza-shop">
<head></head>
<body ng-controller="HomeController">
<div class="container">
<header ng-include="'templates/header.html'">
</header>
<h2>Our Pizzas:</h2>
<div ng-repeat="pizza in pizzas">
<h3>{{ pizza.name }}</h3>
...

!Controllers

Do not use a controller to:

Manipulate the Dom


- Business logic only. Presentation logic affects testability and belongs in directives

Format input
- Use angular form controls instead: https://docs.angularjs.org/guide/forms

Filter output
- Use angular filters instead: https://docs.angularjs.org/guide/filter

Share code or state across controllers


- Use angular services instead: https://docs.angularjs.org/guide/services

Manage the life cycle of other components


- eg: Using a controller to create a service instance

Services
service(class) - registers a constructor function, class that will be
wrapped in a service provider object, whose $get property will
instantiate a new object using the given constructor function.

Stateless, injectable argument providing the


instance of a function passed to
module.service
Singleton: delayed/lazy loaded (Not instantiated
until used), only instantiated once
Good for cross app/controller communication
(sharing of utility functions)

Factories
factory(fn) - registers a service factory function, fn, that will be wrapped in
a service provider object, whose $get property will contain the given
factory function.
service
module.service('MyService', function() {
this.method1 = function() {
//..
}

factory
module.factory('MyService', function() {
var factory = {};
factory.method1 = function() {
//..
}

this.method2 = function() {
//..
}
});

factory.method2 = function() {
//..
}
return factory;
});

Services, Factories, Values, Constants

Are convenience methods for making providers

Serve to avoid polluting the global namespace

Services, Factories, Values, Constants


service vs. factory

The difference between factory and service is the


difference between a function and an object.

Factory(give it a function)

Service(give it a constructor)

Further study (object vs. function):


http://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript

Further study (ng factory vs. service):


http://iffycan.blogspot.com/2013/05/angular-service-or-factory.html
https://www.youtube.com/watch?v=A6cJasNBkyI

Services, Factories, Values, Constants


value vs. constant

Either can be a primitive, object, or function


Constant can be injected into services, controllers, or
module configs
Value can only be injected into services and controllers
Why can't value be injected into module
config?
The injector has not been setup yet.
Constants (special case) are accessed by their
name alone, cannot be changed, and aren't
accessed through a provider $get.

Services, Factories, Values, Constants

Are convenience methods for making providers


You can also create your own providers:
app.config(function($provide){
$provide.provider('MyProvider',{
$get: function(){
return{
foo: function(){
return 'bar';
}
}
}
})
});

Services, Factories, Values, Constants


Source:

function provider(name, provider_) {


assertNotHasOwnProperty(name, 'service');
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
}
return providerCache[name + providerSuffix] = provider_;
}
function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
function value(name, val) { return factory(name, valueFn(val)); }
function constant(name, value) {
assertNotHasOwnProperty(name, 'constant');
providerCache[name] = value;
instanceCache[name] = value;
}

Directives
Isolate Scope: Directive-specific scope

Assists in creating reusable components

Prevents or other components from changing your


model state
app
.controller("myController", function($scope){
$scope.doSomething = function(something){...};
})
.directive('myDirective', function () {
return {
restrict: "E",
scope:{
done:"&"
},
template:'my-template.html'
}
});

The 'scope' option


isolates the directive's
scope.

Directives
Directive Definition Options: Instructions to the ng compiler
app
.controller("myController", function($scope){
$scope.doSomething = function(something){...};
})
.directive('myDirective', function () {
return {
restrict: "E",
priority:sort order before compilation,
terminal: if true, process this directive last,
scope: true, false, or {} (isolate)
controller: instantiate before pre-linking phase...,
require:..another directive and inject its controller...
controllerAs:controller alias at the directive scope...
type: doctype ('html','svg','math'),
template:replace current element with....
templateUrl: same as above, but async from url...
replace: where to insert template...,
transclude: precompile element.....,
compile: transform template Dom...,
link: update Dom, register listeners...,
}

restrict:
matching restriction
options:
'A' - only matches attribute name
'E' - only matches element name
'C' - only matches class name
'M' - only matches comment
or any combination.
(eg: 'AE')
default: 'A'

Directives
Directive Definition Options: Instructions to the
compiler
app
.controller("myController", function($scope){
$scope.doSomething = function(something){...};
})
.directive('myDirective', function () {

Deferring to api:
restrict: "E",
https://docs.angularjs.org/api/ng/service/
priority:sort order before compilation,
terminal: if true, process this directive last,
$compile#description_comprehensive-directivescope: true, false, or {} (isolate)
controller: instantiate
before pre-linking phase...,
api_directive-definition-object
return {

require:..another directive and inject its controller...


controllerAs:controller alias at the directive scope...
type: doctype ('html','svg','math'),
template:replace current element with....
templateUrl: same as above, but async from url...
replace: where to insert template...,
transclude: precompile element.....,
compile: transform template Dom...,
link: update Dom, register listeners...,

Unit Testing
Karma

Test on real devices

Remote control

Testing framework agnostic (Jasmine, Mocha, QUnit...


or write adapter)

Simple CI integration (Jenkins, Travis, Semaphore)

Open Source

Easy to debug from IDE

End-to-End (e2e) Testing


Protractor

Thin wrapper for webdriver.js


Support for cross-browser testing using
Selenium Webdriver
Support for GhostDriver/PhantomJs (Wip)
Includes webdriver-manager for drivers,
Selenium installation, and updates to both
Includes elementExplorer for building locators
Integrates with Saucelabs, easy enough to port
over to BrowserStack

End-to-End (e2e) Testing

Protractor

Recommends the Page Object Model design


pattern for testing

End-to-End (e2e) Testing

Protractor
(Optional) Tap into webdriver.js:
var flow = browser.driver.controlFlow();
// pseudocode
login = function(username){...}
navigateToReviews = function(){...}
addNewReview = function(){...}
flow.execute(login('Walt'));
flow.execute(navigateToReviews);
flow.execute(addNewReview);

Continuous Integration

Jenkins + NodeJs plugin

Unit tests run via Grunt and Karma

e2e tests are still a work-in-progress:

Rely on npm concurrent, connect(express), scripts for setup +


watch and teardown

Awaiting reintroduction of timing in Jasmine for PhantomJs tests

Awaiting Saucelabs/BrowserStack credentials for cross-browser


tests

Real World Experiences: Project


Challenges
Project challenges are similar to those of all
large projects:

Discovering and establishing best practices amongst


contributors
Directory structures/file taxonomy, refactoring with project
growth

Keeping libraries up do date, Bower in the loop

Rediscovery of conveniences found in mature frameworks

Multiple solutions, evaluation, and selection

Real World Experiences: New


Challenges

Adjustments for all


Ramp up on new framework and concepts
Continuous delivery and deployment challenges (binary artifacts, externalizing conf)
Various tools/tools in motion (grunt, node/npm, bower, css compilers, yeoman)

Front-end veterans
New conventions, structures, design patterns (not just jquery, js, css any more)
More responsibility for application architecture

Back-end veterans
Javascript as the primary language (callbacks, promises, debugging, refactoring..)
UX, responsive
Coordination with rest apis and content delivery service

Real World Experiences:Gotchas

Potential for memory leaks/weird behavior with scope misuse

Bower gone wild

Dependency additions, revisions (npm need-to-know)

Library/dependency bloat

Issues/feature requests in (ng and integrated) libraries and tools

Tool Support: IDEs & Refactoring

IntelliJ Idea, WebStorm

Vim, SublimeText

Netbeans

Visual Studio

Behold the
Plug-Ins!

Tool Support: Misc. Open Source


Frameworks

Yeoman (Yo, Grunt, Bower) + generators


http://yeoman.io/

Lots of Grunt plugins

Lots of Npm packages

Batarang plugin for Chrome

Browser web developer console

Tool Support: Static Code Analysis

JsHint: http://www.jshint.com/

grunt-contrib-jshint:
https://github.com/gruntjs/grunt-contrib-jshint

Enforces code quality using rules

Configured tasks in grunt pass directly to jshint

Customizable

reporter output,

options and globals

ignores for specific warnings

grunt-contrib-concat: Linting before and after concatenating files

Tool Support: Code Coverage

Karma + Istanbul: karma-coverage


https://github.com/karma-runner/karma-coverage

Generates reports in several formats including Cobertura xml


(integration with Jenkins)

Protractor
(none, lots of unit tests, REST apis covered on their end)

Resources for Learning

stackoverflow

angularjs.org

AngularJs api docs: https://docs.angularjs.org/api

github (docs, plunkers, demos)

egghead.io (free and subscription)

AngularJs Style Guide:


https://google-styleguide.googlecode.com/svn/trunk/angularjs-google-style.html
angular-phonecat tutorial:
http://docs.angularjs.org/tutorial
Los Techies AngularJs:
http://lostechies.com/gabrielschenker/2014/02/26/angular-js-blog-series-table-of-content/

Q&A

Demo examples: https://github.com/kthblmfld/angularjs-from-scripts

Вам также может понравиться