javascript-ioc

A simple JavaScript IOC that ties constructor arguments (dependencies) to variables or other constructors recursively.


Keywords
ioc, di, di-container, javascript-ioc
License
GPL-3.0
Install
npm install javascript-ioc@2.3.3

Documentation

javascript-ioc

Build npm version

A simple JavaScript IOC that ties constructor arguments (dependencies) to variables or other constructors recursively.

This library is small and has no dependencies on any other package. This library is framework agnostic and is not intended to be used as a service locator. Ideally you'll organize and define your dependency bindings in the bootstrapping of your application and then call the ioc.get method from a single location in your code that gives your chosen framework the constructed object. This can help give your application a more decoupled design. You could even decouple your app from the framework ;) This library has a very simple API that could be replaced by another IOC later if you wish.

Here is some example usage in a small project: Bowling Kata.

Also, here is some example usage from the unit tests:

import { Ioc } from 'javascript-ioc';

...
beforeEach(() => ioc = new Ioc());
...

it("should inject dependencies into classes", () => {
    //Arrange
    Dependency1 = function () {
        this.prop1 = "success1";
    };
    Dependency2 = function () {
        this.prop1 = "success2";
    };
    ioc.bind("dependency1", { toConstructor: Dependency1 });
    ioc.bind("dependency2", { toConstructor: Dependency2 });

    class ClassImpl {
        constructor(dependency1, dependency2) {
            this.prop1 = dependency1.prop1;
            this.prop2 = dependency2.prop1;
        }
    }

    //Act
    var impl = ioc.get(ClassImpl);

    //Assert
    expect(impl.prop1).to.equal("success1");
    expect(impl.prop2).to.equal("success2");
});

it("should get instance by contract name", () => {
    Dependency1 = function () {
        this.prop = "success";
    }

    ioc.bind("DependencyName", { toConstructor: Dependency1 });

    expect(ioc.get("DependencyName").prop).to.equal("success");
});

it("should support nested dependencies recursively", () => {
    //Arrange
    Dependency1 = function () {
        this.prop1 = "success1";
    };
    Dependency2 = function () {
        this.prop1 = "success2";
    };
    Implementation = function (dependency1, dependency2) {
        this.prop1 = dependency1.prop1;
        this.prop2 = dependency2.prop1;
    };
    Implementation2 = function (impl) {
        this.success = impl.prop1;
    };
    ioc.bind("impl", { toConstructor: Implementation });
    ioc.bind("dependency1", { toConstructor: Dependency1 });
    ioc.bind("dependency2", { toConstructor: Dependency2 });

    //Act
    //Assert
    expect(ioc.get(Implementation2).success).to.equal("success1");
});

it("should support shallow ES6 object matching", () => {
    //Arrange
    Dependency1 = function () {
        this.prop1 = "success1";
    };
    Dependency2 = function () {
        this.prop1 = "success2";
    };
    ManualDepImpl = function (dependency0, {dependency1, dependency2}, dependency3) {
        this.prop0 = dependency0;
        this.prop1 = dependency1.prop1;
        this.prop2 = dependency2.prop1;
        this.prop3 = dependency3;
    };
    ManualDepImpl.prototype.dependencies = ["dependency0", ["dependency1", "dependency2"], "dependency3"];
    ioc.bind("dependency0", { toConstant: "success0" });
    ioc.bind("dependency1", { toConstructor: Dependency1 });
    ioc.bind("dependency2", { toConstructor: Dependency2 });
    ioc.bind("dependency3", { toConstant: "success3" });

    //Act
    var result = ioc.get(ManualDepImpl);

    //Assert
    expect(result.prop0).to.equal("success0");
    expect(result.prop1).to.equal("success1");
    expect(result.prop2).to.equal("success2");
    expect(result.prop3).to.equal("success3");
});

it("should support minification via declaring dependencies in prototype.dependencies property", () => {
    //Arrange
    Dependency1 = function () {
        this.prop1 = "success1";
    };
    Dependency2 = function () {
        this.prop1 = "success2";
    };
    ManualDepImpl = function (d1, d2) {
        this.prop1 = d1.prop1;
        this.prop2 = d2.prop1;
    };
    ManualDepImpl.prototype.dependencies = ["dependency1", "dependency2"];
    ioc.bind("dependency1", { toConstructor: Dependency1 });
    ioc.bind("dependency2", { toConstructor: Dependency2 });

    //Act
    var result = ioc.get(ManualDepImpl);

    //Assert
    expect(result.prop1).to.equal("success1");
    expect(result.prop2).to.equal("success2");
});

it("should support binding to constants, not just constructors", () => {
    //Arrange
    ioc.bind("constTest", { toConstant: "constant success" });
    Implementation = function (constTest) { this.prop1 = constTest; };

    //Act
    //Assert
    expect(ioc.get(Implementation).prop1).to.equal("constant success");
});

it("should automatically call bind to constructor or bind to constant", () => {
    //Arrange
    ioc.bind("testConst", { to: { prop: "constant worked" } });
    ioc.bind("testConstruct", { to: function () { this.prop = "constructor works"; } });
    var impl = function (testConst, testConstruct) {
        this.testConst = testConst;
        this.testConstruct = testConstruct
    }

    //Act
    var inst = ioc.get(impl);

    //Assert
    expect(inst.testConst.prop).to.equal("constant worked");
    expect(inst.testConstruct.prop).to.equal("constructor works");
});