Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer...

20
DI IN JS Created by Eugeny Dmitriev

Transcript of Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer...

Page 1: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

DI IN JSCreated by Eugeny Dmitriev

Page 2: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)
Page 3: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

MAY THE BROWSER BE WITH YOU.document.addEventListener("DOMContentLoaded", brewCappuccino);

Framework can provide higher abstraction$routeProvider.when('/, {  templateUrl: 'home.html',  controller: 'HomeCappucinoController'});

Page 4: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

PFFF, I ALREADY HAVE MODULES.var Milk = require('milk'),    CoffeeBeans = require('coffee‐beans'),    Sugar = require('sugar'),    Wager = require('water');

function CappuccinoFactory() {  this.milk = new Milk({fat: ’skim’});  this.beans = new CoffeeBeans({from: ‘Africa’});  this.sugar = new Sugar({type: ‘granulated’});  this.water = new Water();}CappuccinoFactory.prototype.brew = function brewOneCup() {  var brewedCoffee = this.water.add(this.beans.roast()).brew()  var foamFoam = this.milk.add(this.sugar).heat().blend();  brewedCoffee.add(milkFoam);}

Page 5: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

A BIT LIGHTER PATH WITH SERVICE LOCATOR.var injector = require('app').injector;

function CappuccinoFactory(injector) {  this.milkFoam = injector.get(‘milkFoam’);  this.brewedCoffee = injector.get(‘brewedCoffee’);}

CappuccinoFactory.prototype.brew = function brewOneCup() {  brewedCoffee.add(milkFoam);}

Page 6: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

“DON'T CALL US, WE'LL CALL YOU”function CappuccinoFactory(brewedCoffee, milkFoam) {  this.milkFoam = milkFoam;  this.brewedCoffee = brewedCoffee;}CappuccinoFactory.prototype.brew = function brewOneCup() {  brewedCoffee.add(milkFoam);}

var BrewedCoffee = require('brewed‐coffee'),    MilkFoam = require('milk‐foam');CappuccinoFactory.prototype.inject = [BrewedCoffee, MilkFoam];

Page 7: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

ANGULAR STYLERegister bootstrap callback

Find ['ng-app']// All modules are registered including dependency per each module    doBootstrap(){ createInjector(listModules, strictDi) }

// Instances dependency graph//          s1//        /  | \//       /  s2  \//      /  / | \ \//     /s3 < s4 > s5//    ////   s6

Page 8: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

SYNAX// inferred (only works if code not minified/obfuscated)$injector.invoke(function(serviceA){});

// annotatedfunction explicit(serviceA) {};explicit.$inject = ['serviceA'];$injector.invoke(explicit);

// inline$injector.invoke(['serviceA', function(serviceA){}]);

Page 9: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

AUTOMATIONnpm install ‐g ng‐annotate

// @ngInjectfunction Foo($scope) {}

function Foo($scope) {}Foo.$inject = ["$scope"];

x = /*@ngInject*/ function($scope) {};=>x = ["$scope", function($scope) {}];

npm i grunt‐ng‐annotate ‐‐save‐devnpm i gulp‐ng‐annotatenpm i browserify‐ngannotatenpm i ng‐annotate‐loader

Page 10: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

TOMORROW COMES TODAY OR ES777// app.ats is an ATScript draft, maybe already deprecated in favor of typescriptclass MyApp {  server:Server;  @Bind('name') name:string;  @Event('foo') fooFn:Function;

  @Inject()  constructor(@parent server:Server) {}

  greet():string {}}

Page 11: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

WANNA TRY?

Page 12: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

ES6 BY DEFAULT// customer‐detail.es6import {HttpClient} from 'aurelia‐http‐client';export class CustomerDetail{    static inject() { return [HttpClient]; }    constructor(http){        this.http = http;    }}

Even smaller in typescript// customer‐detail.tsimport {HttpClient} from 'aurelia‐http‐client';export class CustomerDetail{    constructor(http:HttpClient){        this.http = http;    }}

Page 13: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

DI CONTAINER FEATURES// Lazy injectionimport {Lazy} from 'aurelia‐framework';import {HttpClient} from 'aurelia‐http‐client';

export class CustomerDetail{    static inject() { return [Lazy.of(HttpClient)]; }    constructor(getHTTP){        this.getHTTP = getHTTP;    }    sendRequest(config) {        getHTTP(http => {            http.get('api/config', config)        })    }}

Page 14: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

DI CONTAINER FEATURES// All injection returns all initialized workersimport {Optional} from 'aurelia‐framework';import {Outsider} from 'outsiders';

export class CustomerDetail{    static inject() { return [Optional.of(Outsider)]; }    constructor(outsiders){        this.outsiders = outsiders || 'no outsiders initialized';    }}

Page 15: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

DI CONTAINER FEATURES// Parentimport {Parent} from 'aurelia‐framework';import {Router} from 'aurelia‐router';

export class CustomerDetail{    static inject() { return [Parent.of(Router)]; }    constructor(router){        this.router = router;    }}

Page 16: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

DI CONTAINER FEATURES// All injection returns all initialized workersimport {All} from 'aurelia‐framework';import {Worker} from 'workers';

export class CustomerDetail{    static inject() { return [All.of(Worker)]; }    constructor(workers){        this.workers = workers;    }}

Page 17: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

DI REGISTRATION FEATURES// default registration is singletonimport {Metadata} from 'aurelia‐framework';import {Woker} from 'workers';

export class CustomerDetail{    static metadata(){ return Metadata.transient(); }    static inject() { return [Worker]; }    constructor(worker){        this.newWorker = worker;    }}

Page 18: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

UNIT TESTINGdescribe('User permissions service specification', function () {    var userServiceMock;    beforeEach(module('myApp', function ($provide) {        userServiceMock = {            getUserDetails: jasmine.createSpy()        };        $provide.value('userService', userServiceMock);    }));    describe('hasAdminAccess method', function () {        it('should return true when user details has property: admin === true',         inject(function (permissionService) {            userServiceMock.getUserDetails.andReturn({admin: true});            expect(permissionService.hasAdminAccess('anAdminUser')).toBe(true);        }));    });});

Page 19: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

NG-IMPROVED-TESTINGdescribe('User permissions service specification', function() {    beforeEach(ModuleBuilder.forModules('myApp')        .serviceWithMocksFor('permissionService', 'userService')        .build());    describe('hasAdminAccess method', function() {        it('should return true when user details has property: admin == true',        inject(function(permissionService, userServiceMock) {            userServiceMock.getUserDetails.andReturn({admin: true});            expect(permissionService.hasAdminAccess('anAdminUser')).toBe(true);        }));    });});

bower install ng‐improved‐testing ‐‐save‐dev

Page 20: Dependency Injection Pattern in JavaScript, Speakers' Corner by Evgeny Dmitriev, Frontend-developer (Ciklum, Minsk)

THE ENDBY EUGENY DMITRIEV