Ultimate Introduction To AngularJS

Post on 10-May-2015

557 views 4 download

Tags:

description

The ultimate introduction to AngularJS with an overview on testing

transcript

The Ultimate Introduction to

AngularJS

Jacopo NardielloPadawan Programmer

Twitter: @jnardiello

Practicing the force at

What Angular is NOT

“AngularJS is not jQuery”

What Angular is NOT

Framework != Library

“A framework embodies some abstract design, with more behavior built in.”

! - M. Fowler

MVC MVVMvs

What is Angular

Superheroic JavaScript MVW Framework

What is Angular

Superheroic JavaScript MVW Framework

Model

What is Angular

Superheroic JavaScript MVW Framework

ViewModel

What is Angular

Superheroic JavaScript MVW Framework

ViewModel

Whatever (works for you)

Zen of Angular

#1“It is a very good idea to decouple

DOM manipulation from app logic.”

#2“It is a really, really good idea to regard app testing as equal in importance to

app writing”

#3“It is an excellent idea to decouple the client side of an app from the server

side”

Elements of an Angular App

Bootstrapping an App

<body ng-app=“car">

JS bootstrap code

angular.module('car', [ 'car.service', 'car.directive', ‘car.controller’ ])

JS bootstrap code

angular.module('car', [ 'car.service', 'car.directive', ‘car.controller’ ])

JS bootstrap code

angular.module('car', [ 'car.service', 'car.directive', ‘car.controller’ ])

JS bootstrap code

angular.module('car', [ 'car.service', 'car.directive', ‘car.controller’ ])

Angular is modular

angular.module('car.service', […])

angular.module(‘car.directives', […])

Extending the DOM

Angular extends DOM behavior using Directives

Directivesangular.module('greetApp', []) .directive(‘greetMe’, function() { return { template: ‘<h1>Hello</h1>’ } })

angular.module('greetApp', []) .directive(‘greetMe’, function() { return { template: ‘<h1>Hello</h1>’ } })

Directives

js

html <div greet-me></div>

angular.module('greetApp', []) .directive(‘greetMe’, function() { return { template: ‘<h1>Hello</h1>’ } })

Directives

Adding logic to directives.directive('myCoffeeMachine', [ ‘$interval’, ‘water’, ‘pump’, function($interval, dateFilter) { return { restrict: 'AE', link: function(scope, element, attrs) { coffee = $interval(function() { prepareCoffee(); // update DOM }, n0000); } }]);

Adding logic to directives.directive('myCoffeeMachine', [ ‘$interval’, ‘water’, ‘pump’, function($interval, dateFilter) { return { restrict: ‘AE', link: function(scope, element, attrs) { coffee = $interval(function() { prepareCoffee(); // update DOM }, n0000); } }]);

Adding logic to directives.directive('myCoffeeMachine', [ ‘$interval’, ‘water’, ‘pump’, function($interval, dateFilter) { return { restrict: 'AE', link: function(scope, element, attrs) { coffee = $interval(function() { prepareCoffee(); }, n0000); } }]);

Reusable element

.directive('myCoffeeMachine', [ ‘$interval’, ‘water’, ‘pump’, function($interval, dateFilter) { return { restrict: 'AE', link: function(scope, element, attrs) { coffee = $interval(function() { prepareCoffee(); // update DOM }, n0000); } }]);

<my-coffee-machine></..>

Controllers

“A Controller is a JavaScript constructor function that is used to augment the Angular

Scope”

Controllers

“A Controller is a JavaScript constructor function that is used to augment the Angular

Scope”

A step back: ScopesScope ~ Model ~ Current state

A step back: ScopesScope ~ Model ~ Current state

Scopes are objects organized hierarchically, aimed to mimic the DOM structure

A step back: Scopes

Each module instantiate a rootScope object

Scope ~ Model ~ Current state

Scopes are objects organized hierarchically, aimed to mimic the DOM structure

A step back: Scopes

Controllersvar myApp = angular.module('myApp',[]); !

myApp.controller('GreetingController', [ '$scope', function($scope) { $scope.greeting = 'Hola!'; }]);

js

Controllersvar myApp = angular.module('myApp',[]); !

myApp.controller('GreetingController', [ '$scope', function($scope) { $scope.greeting = 'Hola!'; }]);

<div ng-controller="GreetingController">

js

html

Angular automagically keeps in sync the model inside controllers with views

Ultimate Data Binding

myApp.controller('GreetingController', [ '$scope', function($scope) { $scope.greeting = 'Hola!'; }]);

<div ng-controller="GreetingController"> {{ greeting }} </div>

Ultimate Data Binding

js

html

Behavior everywhere (and shared resources)

What if you want to declare a class, an object or a function and have it available everywhere?

Behavior everywhere (and shared resources)

Factories

Behavior everywhere (and shared resources)

Factories

Services

Factory Example.factory(‘addOne', function($window) { var sum = 0 return function(sum) { sum++ $window.alert(sum) } })

.factory(‘addOne', function($window) { var sum = 0 return function(sum) { sum++ $window.alert(sum) } })

Factory Example

Behavior everywhere (and shared resources)

Factories

Services

returned value of function

instance

Mounting all the pieces

Testing Angular

Misko Hevery

“Agile Coach at Google where he is responsible for coaching Googlers to maintain the high level of automated testing culture”

- misko.hevery.com/about/

Misko Hevery

+

“Angular is written with testability in mind”

- Angular Doc

Why is Angular easily testable?

Dependency Injection

DI

As a Pattern Framework

DI as Patternfunction Car() { var wheel = new Wheel(); var engine = Engine.getInstance(); var door = app.get(‘Door’); ! this.move = function() { engine.on(); wheel.rotate(); door.open(); } }

DI as Patternfunction Car() { var wheel = new Wheel(); var engine = Engine.getInstance(); var door = app.get(‘Door’); ! this.move = function() { engine.on(); wheel.rotate(); door.open(); } }

DI as Pattern

function Car(wheel, engine, door) { this.move = function() { engine.on(); wheel.rotate(); door.open(); } }

The problemfunction main() { var fuel = new Fuel(); var electricity = new Electricity(); var engine = new Engine(fuel); var door = new Door(Electricity); var wheel = new Wheel(); var car = new Car(wheel, engine, door); car.move(); }

The problemfunction main() { var fuel = new Fuel(); var electricity = new Electricity(); var engine = new Engine(fuel); var door = new Door(Electricity); var wheel = new Wheel(); var car = new Car(wheel, engine, door); car.move(); }

The problemfunction main() { var fuel = new Fuel(); var electricity = new Electricity(); var engine = new Engine(fuel); var door = new Door(Electricity); var wheel = new Wheel(); var car = new Car(wheel, engine, door); car.move(); }

The problemfunction main() { var fuel = new Fuel(); var electricity = new Electricity(); var engine = new Engine(fuel); var door = new Door(Electricity); var wheel = new Wheel(); var car = new Car(wheel, engine, door); car.move(); }

DI as frameworkfunction main() { var injector = new Injector(….); var car = injector.get(Car); car.move(); }

DI as frameworkfunction main() { var injector = new Injector(….); var car = injector.get(Car); car.move(); }

Car.$inject = [‘wheel’, ‘engine’, ‘door’]; function Car(wheel, engine, door) { this.move = function() { … } }

DI as frameworkfunction main() { var injector = new Injector(….); var car = injector.get(Car); car.move(); }

Car.$inject = [‘wheel’, ‘engine’, ‘door’]; function Car(wheel, engine, door) { this.move = function() { … } }

Angular testability is super-heroic!

but…

“you still have to do the right thing.”- Angular Doc

Testability

1. Don’t use new2. Don’t use globals

The Angular Way

Solid structured code

Testing components

Controllerfunction RocketCtrl($scope) { $scope.maxFuel = 100; $scope.finalCheck = function() { if ($scope.currentFuel < $scope.maxFuel) { $scope.check = ‘ko’; } else { $scope.check = 'ok'; } }; }

Controllerfunction RocketCtrl($scope) { $scope.maxFuel = 100; $scope.finalCheck = function() { if ($scope.currentFuel < $scope.maxFuel) { $scope.check = ‘ko’; } else { $scope.check = 'ok'; } }; }

var $scope = {}; var rc = $controller( 'RocketCtrl', { $scope: $scope } ); !$scope.currentFuel = 80; $scope.finalCheck(); expect($scope.check).toEqual('ko');

Controller Test

Directiveapp.directive('rocketLaunchPad', function () { return { restrict: 'AE', replace: true, template: ‘<rocket-launch-pad> Rocket here <rocket-launch-pad>’ }; });

Directive Test

it(‘Check launchpad was installed', function() { var element = $compile(“<rocket-launch-pad></rocket-launch-pad>”)($rootScope); ! expect(element.html()).toContain("Rocket here"); });

Tools

Karma

Protractor

Test Runner

Karma

Run tests against real browsers

Karma

Run tests against real browsers

Unit tests

Config file> karma init

Config file> karma init

- frameworks: [‘jasmine’]

Config file> karma init

- frameworks: [‘jasmine’]- autoWatch: true

Config file> karma init

- frameworks: [‘jasmine’]

- files: [ ‘../tests/controllerSpec.js’ ],

- autoWatch: true

Config file> karma init

- frameworks: [‘jasmine’]

- files: [ ‘../tests/controllerSpec.js’ ],

- autoWatch: true

- browsers: ['Chrome']

Using Karma> karma start config.js

Using Karma> karma start config.js

ProtractorE2E Angular Testing

Angular wrapper for WebDriver

Anatomy of a E2E testdescribe(‘…’, function() { it(‘…’, function() { browser.get(‘…’); element(by.model(‘…’)).sendKeys(..); ! var calculate = element(by.binding(‘…’)); ! expect(some.method()).toEqual(..); }); });

Global Variablesdescribe(‘…’, function() { it(‘…’, function() { browser.get(‘…’); element(by.model(‘…’)).sendKeys(..); ! var calculate = element(by.binding(‘…’)); ! expect(some.method()).toEqual(..); }); });

Global Variablesdescribe(‘…’, function() { it(‘…’, function() { browser.get(‘…’); element(by.model(‘…’)).sendKeys(..); ! var calculate = element(by.binding(‘…’)); ! expect(some.method()).toEqual(..); }); });

Super-Happy Panda!

Page Objects

Protractor provides

Page Objects

Protractor provides

Debugging with superpowers

Page Objects

Protractor provides

Debugging with superpowersAngular-specific functions

Final Thoughts

Jacopo NardielloTwitter: @jnardiello

Questions?