common-injector v1.0.0
common-injector
Heavily influenced by Angular and it's dependency injection.
Inspired by Angular and Indiv.
A lightweight inversion of control container for JavaScript & Node.js apps.
Usage
Install
npm install --save common-injector
Config
If you are using
typescript,common-injectorrequiresTypeScript>= 2.0 and the experimentalDecorators, emitDecoratorMetadata, types and lib compilation options in your tsconfig.json file.{ "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "experimentalDecorators": true, "emitDecoratorMetadata": true } }If you are using
javascriptandwebpack,common-injectorrequires@babel/plugin-proposal-decoratorsand@babel/plugin-proposal-class-propertiesto supportdecoratorsinjavascript.{ loader: 'babel-loader', options: { presets: [ '@babel/preset-env', ], plugins: [ '@babel/plugin-syntax-dynamic-import', ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties', { loose: true }], 'dynamic-import-webpack', ], }, }
Declare dependencies by using the
@InjectableUse decorator
@Injectableto declare a dependency. And all dependencies will be a singleton instance in it's injector.type InjectOptions = { provide?: any; injector?: Injector; }; function Injectable(injectOptions?: InjectOptions): (_constructor: Function) => any;@Injectablewill put a dependency into a defalut injectorrootInjectoras IOC container.@Injectablewill use the class itself as a default token in the IOC container.@Injectableaccepts a parameterinjectOptions: { provide?: any; injector?: Injector; }.You can create other container with an instance which extends
InjectorbyinjectOptions.injector, or set an injection token for this injectable provider byinjectOptions.provide.import { Injectable } from 'common-injector'; class TestServiceToken { public num: number; } @Injectable({ provide: TestServiceToken }) class TestService { public num: number = 3; }Now
TestServicehas been in our default injector, and we should useTestServiceTokenas a token in the IOC container。we can use it as a dependency,and need to use
TestServiceTokenas a token to mark this dependency.Because of using lazy initialization to initialize dependency, please pay attention to the order of dependency.
Inject dependencies into a class by using the
@InjectUse decorator
@Injectto inject a dependency as property of a class.type InjectOptions = { provide?: any; injector?: Injector; }; function Inject(injectOptions?: InjectOptions): (_constructor: any, propertyName: string) => any;@Injectwill get a dependency from a defalut injectorrootInjector.@Injectaccepts a parameterInjectOptions, so you can choose this dependency from which injector withinjectOptions.injectorand useinjectOptions.provideto reset a dependency instead of type of property or useinjectOptions.provideto declare which dependency need to be injected injavascript.
import { Injectable, Inject } from 'common-injector';
class TestServiceToken {
public num: number;
}
@Injectable({ provide: TestServiceToken })
class TestService {
public num: number = 3;
}
class App {
@Inject() private testService: TestServiceToken;
} Because @Inject will set value in __proto__ of Object, so we should not change value of the injected property.
- Create another
Injectoras container
new Injector()We can use another
Injectoras container, and an instance will be a singleton only in it's injector. singleton singleton will be only in thisInjectorFor example, we will creat a injector
otherInjectoras container andTestService2will be put inotherInjectoras a a singleton instance.import { Injectable, Inject, Injector } from 'common-injector'; const otherInjector = new Injector(); @Injectable(otherInjector) class TestService2 { public num: number = 3; }If you want to inject some instances from other
Injector, should useinjectOptions.injectorto declare this instance will be injected from otherInjector.class App { @Inject() private testService: TestServiceToken; @Inject({injector: otherInjector}) private testService2: TestService2; }injector.fork()v0.0.3
We can use public method
fork:() => InjectorofInjector's instance to create a child container.import { rootInjector } from 'common-injector'; const otherInjector = rootInjector.fork();When request a dependency,
injectortries to satisfy that dependency with a provider registered in his owninjector.If this
injectorlacks theprovider, it passes the request up to its parent'sinjector.If that
injectorcan't satisfy the request, it passes the request along to the next parentinjectorup the tree.The request of dependency keeps bubbling up until other
injectorfinds or not find.import { rootInjector } from 'common-injector'; @Injectable() class TestServiceToken { public num: number = 3; } const childInjector = rootInjector.fork(); class App { @Inject({injector: childInjector}) private testService: TestServiceToken; }
Set a constanst in
InjectorIn addition to
@Injectable, with the methodsetInstancefrom instance ofInjector, we can also insert a constant intoInjectordirectly。const otherInjector = new Injector(); class ConstantValue { public aaa: number; } otherInjector.setInstance(ConstantValue, {aaa: 123});class App { @Inject() private testService: TestServiceToken; @Inject({injector: otherInjector}) private testService2: TestService2; @Inject({injector: otherInjector}) private constantValue: ConstantValue; }
Used in javascript
Because of using
Reflect.getMetadata('design:type')to get the type of property, when we usejavascript, this API will be disable.So in javascript,
injectOptions.providecan be used to declare which provide of dependency will be injected.class App { @Inject({provide: TestServiceToken}) testService; @Inject({injector: otherInjector, provide: TestService2}) testService2; }