angularjs - sogeti.nl · summary introduction why angularjs two-way data binding basic concepts...
TRANSCRIPT
THE PRESENTATIONsogeti-summerschool.herokuapp.com/day/2
sogeti-summerschool.herokuapp.com/day/2?print-pdf
SUMMARYIntroductionWhy AngularJSTwo-way data bindingBasic conceptsAngularJS = Awesome
TODAYS AGENDAModulesDependency InjectionUnit testingViews & directivesControllers & scopeFiltersServices
MODULES
ANGULAR MODULESCreating a module:
angular.module('myModule', []);
Getting a module:angular.module('myModule');
DEPENDENCY INJECTION
DEPENDENCY INJECTION
ADVANTAGES DEPENDENCYINJECTION
Consumer only needs knowledge of how to usedependenciesEasier to get loose couplingEasier to testAllows independent development
DEPENDENCY INJECTION INANGULAR
Registering a dependency Using a dependency
angular.module('app', []) .value('Hello', 'Hello, World!');
angular.module('app') .controller('SomeCtrl', function (Hello) this.hello = Hello; );
<div ngcontroller="SomeCtrl as ctrl"> <p>ctrl.hello</p></div>
PROBLEM: MINIFIERS!angular.module('app', []) .value('Hello', 'Hello, World!') .controller('SomeCtrl', function (Hello) this.name = Hello; );
Becomes:
angular.module("app",[]).value("Hello","Hello, World!").controller("SomeCtrl",function(l)this.name=l);
INLINE ARRAY ANNOTATIONangular.module('app', []) .value('Hello', 'Hello, World!') .controller('SomeCtrl', ['Hello', function (Hello) this.name = Hello; ]);
Becomes:
angular.module("app",[]).value("Hello","Hello, World!").controller("SomeCtrl",["Hello",function(l)this.name=l]);
$INJECT PROPERTY ANNOTATIONfunction MyController(Hello) this.name = Hello;MyController.$inject = [ 'Hello'];angular.module('app', []) .value('Hello', 'Hello, World!') .controller('Main', MyController);
UNIT TESTINGJasmineangular-mocks
JASMINEBehavior Driven Development frameworkProvides functions for:
Structuring testsMaking assertions
JASMINETest assertions:
expect([1, 2, 3]).toEqual([1, 2, 3]);
Individual tests:it('should sort in ascending order by default', function () // your test assertion goes here);
Grouping tests:describe('Sorting the list of users', function () // individual tests go here);
ANGULAR-MOCKSProvides support to inject and mock Angular services into
unit testsangular.module('app', []) .controller('PasswordCtrl', function () this.password = ''; this.grade = function () this.strength = (this.password.length > 8 ? 'strong' : 'weak'); ; );
ANGULAR-MOCKSThe module() function
Loads the module it's givendescribe('PasswordCtrl', function () beforeEach(module('app')););
ANGULAR-MOCKSThe inject() function
Creates new instance of dependency per testUsed for resolving references to dependency
describe('PasswordCtrl', function () beforeEach(module('app'));
describe('grade', function () it('should set strength to "strong" if the password is >8 chars', inject(function (_$controller_) var ctrl = _$controller_('PasswordCtrl'); ctrl.password = 'longerthaneightchars'; ctrl.grade(); expect(ctrl.strength).toEqual('strong'); )); ););
VIEWS & EXPRESSIONS
VIEWS & EXPRESSIONS<span ngapp> 1+2=1+2</span>
1+2=3
DIRECTIVES
DIRECTIVESngAppngControllerngModelngIfngRepeatngInitngClick...
DIRECTIVES<div ngapp> <div nginit="names = [ first:'Henk', last:'Bakker', first:'Patrick', last:'de Wit', first:'Patrick', last:'Kraaij', first:'Michael', last:'de Wit' ]"> <ul> <li ngrepeat="name in names"> Name: name.first name.last </li> </ul> </div></div>
Name: Henk BakkerName: Patrick de WitName: Patrick KraaijName: Michael de Wit
DIRECTIVESDocumentation
http://jsbin.sgtifrontend.nl/col/edit?html,js,output
Codepen backup
1. Use ng‐repeat to show the contents of the shoppinglist
2. Use ng‐show to only show the items that aren't done3. Extra:
3.1 Instead of hiding items that are done, use strike-through instead (ng‐class )
3.2 Use ng‐click on an item control wether it'sdone or not
Documentation
CONTROLLERS
SCOPE
SCOPEController
Scope
View
app.controller('MainCtrl', function ($scope) $scope.title = 'Some title';);
title: 'Some title'
<div ngcontroller="MainCtrl"> title </div>
$SCOPE HAS ISSUESDocumentation
http://jsbin.sgtifrontend.nl/yuw/edit?html,js,output
Codepen backup
<div ngcontroller="MainCtrl"> title <div ngcontroller="AnotherCtrl"> title <div ngcontroller="YetAnotherCtrl"> title </div> </div></div>
SCOPING$scope (before v1.2.0) controllerAs (after 1.2.0)
app.controller('MainCtrl', function ($scope) $scope.title = 'Some title';);
app.controller('MainCtrl', function this.title = 'Some title';);
SOLUTION<div ngcontroller="MainCtrl as main"> main.title <div ngcontroller="AnotherCtrl as another"> another.title <div ngcontroller="YetAnotherCtrl as yet"> yet.title </div> </div></div>
CONTROLLERSapp.controller('NameCtrl', function () var names = [ id:0, first:'Henk', last:'Bakker', id:1, first:'Patrick', last:'de Wit', id:2, first:'Patrick', last:'Kraaij', id:3, first:'Michael', last:'de Wit' ]; this.currentName = names[0];
this.nextName = function () this.currentName = names[(this.currentName.id+1)%4]; ;);
Current name: Henk BakkerNext name
LET'S DO SOMETHING!Documentation
http://jsbin.sgtifrontend.nl/yiz/edit?html,js,output
Codepen backup
Create a button that can loop back through the names.
UNIT TESTING CONTROLLERSdescribe('NamesController', function () var namesCtrl; beforeEach(module('app')); beforeEach(inject(function (_$controller_) namesCtrl = _$controller_('NamesController', ); )); it('should have Henk as the default name', function () expect(namesCtrl.currentName.first).toBe('Henk'); ););
UNIT TESTING CONTROLLERShttp://jsbin.sgtifrontend.nl/yes/edit?html,js,output
Codepen backup
Create a test for the 'previous name' feature from the lastassignment
FILTERS
Single filter:
Multiple filters:
Filter with options:
expression | filter
expression | filter | filter | ...
expression | filter:argument1:argument2:...
CUSTOM FILTERSangular.module('app', []).filter('reverse', function () return function (input) var out = []; input.split('').forEach(function (entry) out.unshift(entry); ); return out.join(''); ;);
Usage:
cba
<div ngapp="app">"abc" | reverse</div>
CUSTOM FILTERSDocumentation
http://jsbin.sgtifrontend.nl/fuc/edit?html,js,output
Codepen backup
1. Add a filter feature that capitalizes every oddcharacter index (Sogeti -> SOgEtI)
2. Extra:2.1 Make a 1337-filter, replacing letters with their
1337 number counterparts2.2 Apply both filters to the value in the input box
GOOGLE: "1337 ALPHABET"
UNIT TESTINGdescribe('reverseFilter', function () var reverse; beforeEach(module('app')); beforeEach(inject(function (_$filter_) reverse = _$filter_('reverse'); )); it('should reverse a string', function () expect(reverse('abc')).toBe('cba'); ););
UNIT TESTING A FILTERhttp://jsbin.sgtifrontend.nl/cuk/edit?html,js,output
Codepen backup
1. Change the reverse filter to also capitalize theresulting string
2. Change the unit test for this new functionality3. Extra:
3.1 Implement the 'spacing' filter of the lastassignment
3.2 Unit test the 'spacing' filter (Tip: you can havemultiple describes!)
3.3 Do the same for the 1337-filter
SERVICES
DATA SHARING USING SCOPES
DATA SHARINGUsing parent scope
Controllers
View
angular.module('app', []) .controller('OneCtrl', function ($scope) $scope.$parent.title = 'onectrl'; ) .controller('TwoCtrl', function () );
<div ngcontroller="OneCtrl"> $parent.title </div><div ngcontroller="TwoCtrl"> <button ngclick="$parent.title = 'twoctrl'">twoctrl</button</div>
onectrltwoctrl
DATA SHARINGUsing a service
Controllerwithservice
View
app.service('OneSrv', function () this.title = 'service title'; ) .controller('OneCtrl', function (OneSrv) this.service = OneSrv; );
<div ngcontroller="OneCtrl as ctrl"> ctrl.service.title <button ngclick="ctrl.service.title = 'controller title'">onectrl</div>
EXAMPLE SERVICEangular.module('app', []).service('NumbersList', function () var nrs = [1, 2, 3, 4, 5]; this.getNumbers = function () return nrs; ;);
LET'S GET TO IT!Documentation
http://jsbin.sgtifrontend.nl/yed/edit?html,js,output
Codepen backup
1. Add a service property which changes the computefunction to return a lower-case output
2. Add checkboxes for the controllers which control thisservice property
UNIT TESTING SERVICESdescribe('NumbersList', function () var NumbersList; beforeEach(module('app')); beforeEach(inject(function (_NumbersList_) NumbersList = _NumbersList_; )); it('should return the correct 5 numbers', function () expect(NumbersList.getNumbers()).toEqual([1, 2, 3, 4, 5]); ););
UNIT TESTING SERVICEShttp://jsbin.sgtifrontend.nl/lah/edit?html,js,output
Codepen backup
1. Add a setter function for the nrs var in the service2. Write a unit test for the setter function3. Extra:
3.1 Add a sum function3.2 Write a unit test for the sum function
OBJECT CREATOR TYPESConstantValueServiceFactoryProvider
CONSTANTDefinition
app.constant('MOVIE_TITLE', 'The Matrix');
Unit testapp.controller('MyController', function (MOVIE_TITLE) expect(MOVIE_TITLE).toEqual('The Matrix'););
VALUEDefinition
app.value('movieTitle', 'The Matrix');
Unit testapp.controller('MyController', function (movieTitle) expect(movieTitle).toEqual('The Matrix'););
SERVICEDefinition
app.service('movie', function () this.title = 'The Matrix';);
Unit testapp.controller('MyController', function (movie) expect(movie.title).toEqual('The Matrix'););
FACTORYDefinition
app.factory('movie', function () var private = 'The Matrix'; return title: private ;);
Unit testapp.controller('MyController', function (movie) expect(movie.title).toEqual('The Matrix'););
PROVIDERDefinition
app.provider('movie', function () var version; this.setVersion: function (value) version = value; ;
this.$get: function () var private = 'The Matrix '; return title: private + version ; ;).config(function (movieProvider) movieProvider.setVersion('Reloaded'););
Unit testapp.controller('MyController', function (movie) expect(movie.title).toEqual('The Matrix Reloaded'););
SUMMARYModulesDependency InjectionUnit testingViews & directivesControllers & scopeFiltersServices
THANKS!See you all next week!