AngularJS - · PDF fileSUMMARY Modules Dependency Injection Unit testing Views & directives...

42
AngularJS SOGETI ANGULARJS TRAINING DAG 3 - ADVANCED ANGULAR

Transcript of AngularJS - · PDF fileSUMMARY Modules Dependency Injection Unit testing Views & directives...

AngularJS

SOGETI ANGULARJS TRAININGDAG 3 - ADVANCED ANGULAR

THE PRESENTATIONsogeti-summerschool.herokuapp.com/day/3

sogeti-summerschool.herokuapp.com/day/3?print-pdf

SUMMARYModulesDependency InjectionUnit testingViews & directivesControllers & scopeFiltersServices

TODAYS AGENDACustom directivesPromisesToolingLearning resources

CUSTOM DIRECTIVESAngular directives: ng-repeat, ng-if, ng-init, etc.

PROBLEM<div ng­controller="ProductListController as productsCtrl"> <h2>Products</h2>

<p>Showing productsCtrl.products.length of productsCtrl.total products</p>

<div class="product­container"> <div class="product" ng­repeat="product in productsCtrl.products"> <h5>product.title</h5> <em>Price: product.price | currency</em> <img ng­src="product.imageLink"> <span>Stock: product.stock</span> <ul> <li ng­repeat="feature in product.features"> feature.title: <strong>feature.value</strong> </li> </ul> </div> </div> <p>Page: productsCtrl.page <a href="#/page/productsCtrl.page ­ 1">&lt;</a> <a href="#/page/productsCtrl.page + 1">&gt;</a> </p></div>

SOLUTION<div ng­controller="ProductPageController as productPageCtrl"> <h2>Products</h2>

<p>Showing productPageCtrl.products.length of productPageCtrl.total products</p>

<div class="product­container"> <product ng­repeat="product in productPageCtrl.products"></product> </div>

<pagination page="productPageCtrl.page"></pagination></div>

SOLUTION<div class="product"> <h5>product.title</h5> <em>Price: product.price | currency</em> <img ng­src="product.imageLink"> <span>Stock: product.stock</span> <ul> <li ng­repeat="feature in product.features"> feature.title: <strong>feature.value</strong> </li> </ul></div>

SOLUTIONapp.directive('product', function () return templateUrl: 'product.template.html' // Or use 'template' with the contents directly ;)

LET'S GET TO IT!Documentation

http://jsbin.sgtifrontend.nl/wago/edit?html,js,output

1. See the list of properties? Try and make a 'property'directive:

<li property ng­repeat="...."></li>

DIRECTIVE DEFINITION OBJECTtemplate or templateUrlrestrictscopecontrollerreplacecompilelink

SCOPING<div ng­controller="SomeController"> <span>Controller</span> <input ng­model="title"> title <span>Directive</span> <some­directive></some­directive></div>

app.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ng­model="title"> title' ;);

Controller (parent) test test

Directive (child) test test

SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ng­model="title"> title', scope: false ;);

<some­directive></some­directive>

Controller (parent) test test

Directive (child) test test

SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ng­model="title"> title', scope: true ;);

<some­directive></some­directive>

Controller (parent) test test

Directive (child) test test

SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ng­model="title"> title', scope: title: '@myTitle' ;);

<some­directive my­title="title"></some­directive>

Controller (parent) test test

Directive (child) test test

SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ng­model="title"> title', scope: title: '=myTitle' ;);

<some­directive my­title="title"></some­directive>

Controller (parent) test test

Directive (child) test test

SCOPE (ISOLATED SCOPE)@ - Bind local scope to DOM attribute value

<my­directive my­attr="hello name"></my­directive>

scope: localModel: '@myAttr'

= - Bi-directional binding to parent scope<my­directive my­attr="parentModel"></my­directive>

scope: localModel: '=myAttr'

LET'S GET TO IT!Documentation

http://jsbin.sgtifrontend.nl/hewi/edit?html,js,output

1. Note the way how we call the directives from theHTML

2. Note the way how the scope variables are boundwithin the directive

3. To reset: press the "Run with JS" button in the top rightcorner of the output

TESTING DIRECTIVESapp.directive('sumHeader', function () return template: '<h1>1 + 1</h1>' ;);

describe('Unit testing directive', function () var $compile, $rootScope; beforeEach(module('myApp'));

beforeEach(inject(function (_$compile_, _$rootScope_) $compile = _$compile_; $rootScope = _$rootScope_; ));

it('Compiles correctly', function () var element = $compile("<sum­header></sum­header>")($rootScope); $rootScope.$digest(); expect(element.html()).toContain("2"); ););

ASYNCHRONOUS JAVASCRIPTNetworking operationsScheduled functionsOften using callbacks

CALLBACK// An example of a lengthy task (like a heavy HTTP request),// emulated using window.setTimeout()function slowTask(callback) setTimeout(callback, 3000);// This will obviously be printed firstconsole.log('Starting slow task...');

// Here we execute the lengthy taskslowTask( // This function will only be called once the task is done function () // This will print last console.log('Task done!'); );

// This will print immediately after the first console.log()console.log('Task running...');

Start slow function

TASK NOT STARTED YET...

PROBLEMfunction organizeParty() getWeatherForecast('tomorrow', function (error, forecast) if (error) console.error('Error: ' + error); if(forecast === 'good') planParty('tomorrow', function (error, plans) if (error) console.error('Error: ' + error); else getFriendsList(function (error, friends) if (error) console.error('Error: ' + error); else invite(friends, function (error, plans) // etc, etc, etc.... ); ); ); );

SOLUTION: PROMISESA promise to complete a taskEasy access to task statusFlatten nested callbacks

A PROMISEContains:

Status (pending, fulfilled, rejected)Function referenceCallback references for:

then (fulfilled)catch (rejected)finally (always)

PROMISESvar promise = $q( // Promise function reference function (resolve, reject) // AngularJS alternative for window.setTimeout() $timeout( // Delayed callback function function () // Either 1 or 0 var result = Math.round(Math.random()); if (result === 1) // Fulfill the promise resolve(); else // Reject the promise reject(); , 3000 ); );

promise.then(function () console.log('Done!'););

CHAININGthen, catch, and finally return promises

somePromise .then(onComplete) .then(afterOnComplete) .catch(onReject) .finally(cleanUp);

Return values are forwardedgetWeatherForecast() .then(function (forecast) console.log('Forecast is: ' + forecast); return forecast.temperature; ) .then(function (temperature) console.log('Temperature is: ' + temperature); ) .catch(function (error) console.error('Error occurred: ' + error); );

SHORTHANDSCreating a promise that will always be resolved with a

valuevar promise = $q.when(value);

Creating a promise that will always be rejected with avalue

var promise = $q.reject(value);

PLANNING A PARTYfunction organizeParty() return getWeatherForecast('tomorrow') .then(function (forecast) if (forecast === 'good') return 'tomorrow'; return $q.reject('Bad weather'); ) .then(planParty) .then(getFriendsList) .then(invite) .catch(function (error) console.error(error); return $q.reject(error); );

NOW LET'S TRY IT!http://jsbin.sgtifrontend.nl/zive/edit?html,js,output

1. Add another date value, yesterday, and change theweather forecast function to return an error (rejectedpromise) when yesterday is selected

2. Add more 'slow' functions (use previous partyplanning examples)

3. Add more 'then' steps to planParty() function4. Make sure your error flow is still working properly

PRACTICAL CASEA data service, connecting to a RESTful API

function DataService($http, $q) this.getData = function getData() return $http.get('/some/api/endpoint') .then(function (response) return response.data; ) .catch(function (response) return $q.reject(response.data); ); ;

DataService.$inject = ['$http', '$q'];angular.module('app', []) .service('DataService', DataService);

PRACTICAL CASEGetting the data in a controller

function DataController(DataService) var vm = this;

DataService.getData() .then(function (data) vm.data = data; ) .catch(function (error) vm.error = error; );

DataController.$inject = ['DataService'];angular.module('app') .controller('DataController', DataController);

PRACTICAL CASETesting your data service

describe('DataService', function () var $httpBackend, DataService;

beforeEach(module('testModule')); beforeEach(inject(function (_$httpBackend_, _DataService_) $httpBackend = _$httpBackend_; DataService = _DataService_; ));

it('should retrieve data', function () successFn = jasmine.createSpy('successFn'); failureFn = jasmine.createSpy('failureFn');

$httpBackend.expectGET('/some/api/endpoint').respond(200, ); DataService.getData().then(successFn).catch(failureFn); $httpBackend.flush();

expect(successFn).toHaveBeenCalledWith(); expect(failureFn).not.toHaveBeenCalled(); ););

TESTING DATA SERVICES$http documentation

$httpBackend documentation

http://jsbin.sgtifrontend.nl/joh/edit?js,output

1. Add another async function to the service, sendDatawhich will do a POST request

2. Test the new sendData function3. Extra:

3.1 Test the error flow of both async functions (youcan return error HTTP status codes)

TOOLINGIDEBuild pipeline: task runners

GruntGulp

Plugins

TASK RUNNERSCode quality checkingConcatenationMinificationAutomated testing

Optimize your project for production!

CODE QUALITYJSHintESLintJSCS

AUTOMATED TESTINGUnit testing

Test runner: KarmaRun in watch modePhantomJS: the headless browser

AUTOMATED TESTINGEnd-to-end (E2E) testing

Real in-browser testsTest runner: ProtractorSelenium

POPULAR PLUGINSRouting libraries

ngRoute (angular-route)AngularUI Router (ui-router)

Animation / UX librariesngAnimate (angular-animate)AngularUI BootstrapngMaterial (angular-material)

API librariesngResource (angular-resource)RESTAngular

SUMMARYCustom directivesPromisesToolingLearning resources

ENJOY ANGULARJS!