Skip to content

scaljeri/javascript-dependency-injection-deprecated

Repository files navigation

Get this with Bower

Build Status Coverage Status Dependency Status devDependency Status Code Climate Inline docs

Gitter

Javascript Dependency Injection library written in ES2015

NOTE: This library is deprecated in favor of di-xxl. It is no longer maintained!!

You can find a demo, documentation and a code coverage report here

DI-XXL is a dependency injection library which makes classes accessible by a contract. Instances are created when requested and dependencies are injected, facilitating lazy initialization and loose coupling between classes --> maintainable and testable code!!!!

The Basics

DI-XXL is an extremely versatile library; it provides many ways to implement dependency injection. Choose whatever fits your needs best.

Example 1

class Foo {
    sum(a, b) { return a + b }
}

class Bar {
    constructor(base) {
        this.inject = ['$foo']; // <- list of contracts
        this.total = base;
    }
     
    add(val) {
        this.total = this.inject.$foo.sum(this.total, val);
    }
}

di.register('$foo', Foo);
di.register('$bar', Bar); 

let bar = di.getInstance('$bar', 100);
bar.add(1); // bar.total === 101

During registration the constructor is inspected and the line this.inject = ['$foo']; is parsed. Next, just after the instance is created, the inject array is replaced with an object holding all requested dependencies.

NOTE: In this situation the dependencies cannot be used inside the constructor because they are injected after the instance is created.

However, if you need dependencies in the constructor you have a couple of ways to achieve this

Example 2

class Bar {
    constructor(base) {
        this.total = this.inject.$foo.add(0, base); // Here the dependency is used!
    }
     
    add(val) {
        this.total = this.inject.$foo.sum(this.total, val);
    }
}
di.register('$bar', Bar, {inject: ['$foo']});

Or you can do the following

Example 3

class Bar { 
    constructor($foo, base) {
        this.$foo = $foo;     // $foo instanceof Foo (injected)
        this.total = base;
    }
    
    add(val) {
        this.total = this.$foo.sum(this.total, val);
    }
}
di.register('$bar', Bar);

let bar = di.getInstance('$bar', 100);
bar.add(1); // bar.total === 101

This time DI-XXL doesn't know what do to, in which case it extracts and inspects the constructor arguments.

This pattern can be extended to the instance methods too

Example 3

class Bar {
    constructor(base) { this.total = base; }
  
    add($foo, val) {
      this.total = $foo.sum(this.total, val);
    }
}
di.register('$bar', Bar, { augment: true }); // or a list of instance methods

let bar = di.getInstance('$bar', 10);
bar.add(100); // -> bar.total === 110

It is also possible to define the constructor arguments yourself

Example 4

class Bar {
  constructor(someInstance, someValue) {
      this.someInstance = someInstance;
      this.someValue = someValue;
  }
  ...
}

di.register('$bar', Bar, ['$foo' ,100]);
var bar = di.getInstance('$bar');
bar.someInstance instanceOf Foo
bar.someValue === 100;

Checkout the unit tests for more advanced examples

Singletons

DI can also return the same instance (singleton)

 di.register('$bar', Bar, { singleton: true });
 di.getInstance('$bar') === di.getInstance('$bar')

Factories

A class can produce instances of an other class

 class Bar {
     getFoo(input) {
         return new Foo(input);
     }
 }

In order to rewrite this using DI, Factories come to the rescue

 class Bar {
     constructor(fooFactory) { this.fooFactory = fooFactory; }
      
     getFoo(input) { 
        return this.fooFactory(input);  
     }
 }

and the relations are now defined as follows

 di.register('$bar', Bar, ['$fooFactory']);
 
 di.register('$foo', Foo);

That's all, the Foo-factory is created auto magically!

If you really want to create a factory yourself, you can

 di.register('$fooFactory', ['list', 'of', 'params'], { factoryFor: '$bar' });

Parameters

Now things get a bit tricky, because parameters can be set at different places and some kind of parameter-inheritance happens. The first level of where parameters can be defined is with register

di.register('$bar', Bar, ['$foo', 

For example

 di.register('$bar', Bar, ['p1', 'p2', 'p3', 'p4']);
 let bar = di.getInstance('$bar', 'p5', 'p6', 'p7');

The getInstance method accepts constructor arguments too. The above is equivalent o

  new Bar('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7');

The parameters are added. But what if you like to replace the initial parameter?

 di.register('$bar', Bar, ['p1', 'p2', 'p3', 'p4'], { writable: true });
 let bar = di.getInstance('$bar', 'p5', 'p6', 'p7');

This time the constructor arguments are

 'p5', 'p6', 'p7', 'p4'

Important to note here is that an initial parameter is only replaced if the new parameter not equals undefined.

With factories, you have this behavior too, but also an extra inheritance layer. Check this out

di.register('$barFactory', ['p1', 'p2', 'p3', 'p4', 'p5'], { factoryFor: '$bar' });    
let barFactory = di.getInstance('$barFactory', 'p6', 'p7');                            
let bar = barFactory('p8', 'p9');  // bar is initialized with 'p1', 'p2', ...., 'p9'   

For more advanced use-cases checkout the unit tests file.

Installation

Install this library with npm

$> npm install --save javascript-dependency-injection@beta

or

$> bower install javascript-dependency-injection#2.0.0-rc.1

Unit testing

$> npm test

Documentation

$> npm run doc

About

Javascript dependency injection library

Resources

Stars

Watchers

Forks

Packages

No packages published