Post on 06-Feb-2018
THE PRESENTATIONsogeti-summerschool.herokuapp.com/day/3
sogeti-summerschool.herokuapp.com/day/3?print-pdf
PROBLEM<div ngcontroller="ProductListController as productsCtrl"> <h2>Products</h2>
<p>Showing productsCtrl.products.length of productsCtrl.total products</p>
<div class="productcontainer"> <div class="product" ngrepeat="product in productsCtrl.products"> <h5>product.title</h5> <em>Price: product.price | currency</em> <img ngsrc="product.imageLink"> <span>Stock: product.stock</span> <ul> <li ngrepeat="feature in product.features"> feature.title: <strong>feature.value</strong> </li> </ul> </div> </div> <p>Page: productsCtrl.page <a href="#/page/productsCtrl.page 1"><</a> <a href="#/page/productsCtrl.page + 1">></a> </p></div>
SOLUTION<div ngcontroller="ProductPageController as productPageCtrl"> <h2>Products</h2>
<p>Showing productPageCtrl.products.length of productPageCtrl.total products</p>
<div class="productcontainer"> <product ngrepeat="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 ngsrc="product.imageLink"> <span>Stock: product.stock</span> <ul> <li ngrepeat="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 ngrepeat="...."></li>
SCOPING<div ngcontroller="SomeController"> <span>Controller</span> <input ngmodel="title"> title <span>Directive</span> <somedirective></somedirective></div>
app.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ngmodel="title"> title' ;);
Controller (parent) test test
Directive (child) test test
SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ngmodel="title"> title', scope: false ;);
<somedirective></somedirective>
Controller (parent) test test
Directive (child) test test
SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ngmodel="title"> title', scope: true ;);
<somedirective></somedirective>
Controller (parent) test test
Directive (child) test test
SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ngmodel="title"> title', scope: title: '@myTitle' ;);
<somedirective mytitle="title"></somedirective>
Controller (parent) test test
Directive (child) test test
SCOPINGapp.controller('SomeController', function ($scope) $scope.title = 'test';).directive('someDirective', function () return template: '<input ngmodel="title"> title', scope: title: '=myTitle' ;);
<somedirective mytitle="title"></somedirective>
Controller (parent) test test
Directive (child) test test
SCOPE (ISOLATED SCOPE)@ - Bind local scope to DOM attribute value
<mydirective myattr="hello name"></mydirective>
scope: localModel: '@myAttr'
= - Bi-directional binding to parent scope<mydirective myattr="parentModel"></mydirective>
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("<sumheader></sumheader>")($rootScope); $rootScope.$digest(); expect(element.html()).toContain("2"); ););
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.... ); ); ); );
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)
TASK RUNNERSCode quality checkingConcatenationMinificationAutomated testing
Optimize your project for production!
POPULAR PLUGINSRouting libraries
ngRoute (angular-route)AngularUI Router (ui-router)
Animation / UX librariesngAnimate (angular-animate)AngularUI BootstrapngMaterial (angular-material)
API librariesngResource (angular-resource)RESTAngular
WANT TO KNOW MORE?AngularJS in Design PatternsAdvanced testing in AngularJSJohn Papa's AngularJS style guideDirective attribute bindingng-bookThe Top 10 Mistakes AngularJS Developers MakePromises