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);
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
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>