container-ioc
is a Dependency Injection / Inversion of Control (IoC) container package for Javascript and Node.js applications powered by Typescript . It manages the dependencies between classes, so that applications stay easy to change and maintain as they grow.
Features:
- Well-known Angular DI API.
- No external dependencies.
- Life Time control.
- Hierarchical containers.
- Resolves values using Classes, Factories and Values.
- Descriptive error messages.
- 97% test coverage.
Examples:
Installation:
npm install --save container-ioc
Basics:
Code examples below are written in Typescript. Check examples/javascript for examples written in Javascript.
Step 1. Define your interfaces and types.
Possible values for types: Symbol, string, Object.
interface IApplication {
run(): void;
}
interface IService {
serve(): void;
}
const TApplication = Symbol('IApplication');
const TService = Symbol('IService');
Step 2. Declare dependencies with decorators Injectable and Inject.
import { Injectable, Inject } from 'container-ioc';
@Injectable()
export class Application implements IApplication {
constructor(@Inject(TService) private service: IService) {}
run(): void {
this.service.serve();
}
}
@Injectable()
export class Service implements IService {
serve(): void {
// serves
}
}
Step 3. Create a container and register types in there.
import { Container } from 'container-ioc';
let container = new Container();
container.register([
{ token: TApplication, useClass: Application },
{ token: TService, useClass: Service }
]);
Step 4. Resolve value from the container.
let app = container.resolve(TApplication);
app.run();
Step 2 for Javascript.
Since Javascript does not support parameter decorators, use alternative API for declaring dependencies. In this case we don't use Inject decorator. See examples/javascript for more.
@Injectable([TService])
class Service {
constructor(service) {
this.service = service;
}
}
Life Time control
By default, containers resolve singletons when using useClass and useFactory. Default life time for all items in a container can be set by passing an option object to it's contructor with defailtLifeTime attribute. Possible values: LifeTime.PerRequest (resolves instances) and LifeTime.Persistent (resolves singletons);
import { LifeTime } from 'container-ioc';
const container = new Container({
defaultLifeTime: LifeTime.PerRequest
});
You can also specify life time individually for each item in a container by specifying lifeTime attribute.
container.register([
{
token: TService,
useClass: Service,
lifeTime: LifeTime.PerRequest
}
]);
container.register([
{
token: TService,
useFactory: () => {
return {
serve(): void {}
}
},
lifeTime: LifeTime.Persistent
}
]);
Hierarchical containers
If a container can't find a value within itself, it will look it up in ascendant containers. There a 3 ways to set a parent for a container.
1. Container.createChild() method.
const parentContainer = new Container();
const childContainer = parentContainer.createChild();
2. Container.setParent() method.
const parent = new Container();
const child = new Container();
child.setParent(parent);
3. Via Container's constructor with options.
const parent = new Container();
const child = new Container({
parent: parent
});
Using Factories
/* Without injections */
container.register([
{
token: 'TokenForFactory',
useFactory: () => {
return 'any-value';
}
}
]);
/* With injections */
container.register([
{ token: 'EnvProvider', useClass: EnvProvider },
{
token: 'TokenForFactory',
useFactory: (envProvider) => {
// do something
return 'something';
},
inject: ['EnvProvider']
}
]);
Using Values
container.register([
{ token: 'IConfig', useValue: {}}
]);
Shortcut for Classes
container.register([
App
]);
Is the same as:
container.register([
{ token: App, useClass: App }
]);
Contribution
Become a contributor to this project. Feel free to submit an issue or a pull request.
see CONTRIBUTION.md for more information.
Please see also our Code of Conduct.