AngularJS: Good parts

35
AngularJS: Good parts Евгений Жарков DOOR3 @2j2e [email protected]

Transcript of AngularJS: Good parts

AngularJS: Good parts

Евгений Жарков DOOR3

@2j2e [email protected]

Watchers ]:>

Track Watchers

Angular adds a watcher to the digest cycle for each of these:• {{expression}} — templates• $scope.$watch — in the code

Track WatchersfunctiongetWatchers(root){root=angular.element(root||document.documentElement);varwatcherCount=0;functiongetElemWatchers(element){varisolateWatchers=getWatchersFromScope(element.data().$isolateScope);varscopeWatchers=getWatchersFromScope(element.data().$scope);varwatchers=scopeWatchers.concat(isolateWatchers);angular.forEach(element.children(),function(childElement){watchers=watchers.concat(getElemWatchers(angular.element(childElement)));});returnwatchers;}functiongetWatchersFromScope(scope){if(scope){returnscope.$$watchers||[];}else{return[];}}returngetElemWatchers(root);}

https://gist.github.com/kentcdodds/31c90402750572107922

Track Watchers

//getallwatchersonthewholepagegetWatchers();//getwatchersofaspecificelement(anditschildren)getWatchers(document.body);//selecttheelementofinterestinChromeDevtoolsgetWatchers($0);

Track Watchers

https://github.com/kentcdodds/ng-stats

AngularJS > ES6

ES6, require.js

functionMainController(){……………}

export{MainController}

—————————————————————————————————————

import{MainController}from‘./path/to/MainController';……………

ES6, require.jsclassMainController{

constructor(searchService){this.searchService=searchService;}

search(){this.searchService.fetch(this.searchTerm).then(response=>{this.items=response.data.items;});}}

export{MainController}

ES6, require.js

import{MainController}from'./MainController';

import{SearchService}from'./SearchService';

angular

.module('app',[])

.controller('mainController',MainController)

.service('searchService',SearchService);

Inheritance

classPageController{

constructor(title){

this._title=title;}

title(){

return'Title:'+this._title;}}

export{PageController}

Inheritance

import{PageController}from'./PageController';

classProductPageControllerextendsPageController{

constructor(){

super('ES6inheritancewithAngular’);

}}

export{ProductPageController}

Inheritance

import{ProductPageController}from'./ProductPageController';

angular

.module('app',[])

.controller('ProductPageController',ProductPageController);

Service Inheritance

myModule.service(fn)

YES, instantiated with the new operator under the hood

myModule.factory(fn)

NO

Don’t forget about minification

MainController.$inject=['SearchService'];

and/or ng-annotate

exportdefaultclassNameService{

/*@ngInject*/

constructor($q){..}

}

ES6 Babel Browserify Boilerplate

https://github.com/thoughtram/es6-babel-browserify-boilerplate

Angular ES6

https://github.com/michaelbromley/angular-es6

AngularJS > ES6 > Tests

ES5, Karma, Jasmine, PhantomJSdescribe('TodoService',function(){

varTodoService,InitialTodosMock;//InstantiateAngularJScontextbeforeEach(module("app"));//RegistermocksinAngularJScontextbeforeEach(module(function($provide){InitialTodosMock=[{label:'Testtodo',done:false}];$provide.value('initialTodos',InitialTodosMock);}));//GetinstanceofTodoServicewithmockeddependenciesfromAngularJScontextbeforeEach(inject(function(_TodoService_){TodoService=_TodoService_;}));//Oh,...dotheactualtesting!!!it('shouldhaveinitialtodo',function(){expect(TodoService.todos.length).toBe(1);expect(TodoService.todos[0].label]).toBe('Testtodo');expect(TodoService.todos[0].done]).toBe(false);

});});

ES5, Karma, Jasmine, PhantomJSdescribe('TodoController',function(){varscope,$rootScope,$controller;//InstantiateAngularJScontextbeforeEach(module('app'));//RegistermocksinAngularJScontext//(sometimesnotnecessary,wecanuserealservicestoo,buttheAngularcontextgrows...)beforeEach(module(function($provide){varTodoServiceMock={todos:[],addTodo:function(){/*……*/},toggleTodo:function(){/*……*/},removeDoneTodost(){/*……*/}};$provide.value('TodoService',TodoServiceMock);}));//GetinstanceofTodoController,youknow,createnew$scopefrom$rootScopebyyourselfandstuff...//Itispossibletonotuse$scopewhenusing'controllerAs'syntax,//butyoustillhavetouseatleast$controllertogettherefferencetocontrolleritselfbeforeEach(inject(function(_$rootScope_,_$controller_,_TodoService_){$controller=_$controller_;$rootScope=_$rootScope_;scope=$rootScope.$new();

$controller('TodoController',{$scope:scopeTodoService:_TodoService_});}));//Oh,...dotheactualtesting!!!it('shouldhaveinitialtodos',function(){expect(scope.todos.length).toBe(1);});});

Issues

• Angular context module(‘app’) must be instantiated to be able to do any testing. Without Angular context you can’t get access (reference) to your controllers / services.

• Angular and all other used libraries must be included during testing so that it is even possible to instantiate Angular context.

• Angular context can grow quite large so that it’s creation will consume considerable amount of time for every test file.

• Karma exclusion syntax doesn’t follow standard node glob pattern which can make you go crazy when you try to solve timeout errors caused by insufficient memory on PhantomJS by splitting test execution into multiple batches, while supporting dev mode single test execution (karma uses extra exclude property instead of supporting standard “!”)

ES6, Mocha, chaiimport{assert}from'chai';importTodoServicefrom'./todo.service.js';

letservice;

describe('TodoService',function(){beforeEach(function(){service=TodoService();});

it('shouldcontainemptytodosafterinitialization',function(){assert.equal(service.todos.length,0);});

it('shouldtoggletodo',function(){service.addTodo('Finishexampleproject');assert.equal(service.todos[0].done,false);service.toggleTodo('Finishexampleproject');assert.equal(service.todos[0].done,true);service.toggleTodo('Finishexampleproject');assert.equal(service.todos[0].done,false);});});

ES6, Mocha, chaiimport{assert}from'chai';importSomeComponentfrom‘./some-component';

letcomponent;

describe('some-component',function(){beforeEach(function(){component=newSomeComponent();});

it('shouldstartwithcountervalue20',function(){assert.equal(component.counter,20);});

it('shouldacceptinitialcountervalueasdependency',function(){component=newSomeComponent(30);assert.equal(component.counter,30);});

it('shouldincrementcountervalueafterincrementiscalled',function(){assert.equal(component.counter,20);component.increment();assert.equal(component.counter,21);});});

Dependencies are passed explicitly as a parameter of function

Dig, read, criticise

• Paginationhttps://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination

• angular-formly http://angular-formly.com/

• angular-translatehttps://angular-translate.github.io

• LumX (material design) http://ui.lumapps.com

angular-formly

<formly-formmodel="vm.user"fields="vm.userFields">

<buttontype="submit"class="btnbtn-default"ng-click="vm.submit(vm.user)">Submit</button>

</formly-form>

angular-formlyvm.userFields=[{key:'email',type:'input',templateOptions:{type:'email',label:'Emailaddress',placeholder:'Enteremail'}},{key:'password',type:'input',templateOptions:{type:'password',label:'Password',placeholder:'Password'}},

{key:'file',type:'file',templateOptions:{label:'Fileinput',description:'Exampleblock-levelhelptexthere',url:'https://example.com/upload'}},{key:'checked',type:'checkbox',templateOptions:{label:'Checkmeout'}}];

Pagination

<ANY

dir-paginate="expression|itemsPerPage:(int|expression)[:paginationId(stringliteral)]"

[current-page=""]

[pagination-id=""]

[total-items=""]>

...

</ANY>

Pagination

<dir-pagination-controls

[max-size=""]

[direction-links=""]

[boundary-links=""]

[on-page-change=""]

[pagination-id=""]

[template-url=""]

[auto-hide=""]>

</dir-pagination-controls>