Module
In essence, Container
is a module. Module will help you control the accessibility of provider easier.
#
1. Providers in moduleimport { Module, Container } from '@cellularjs/di';
class Foo { }
@Module({ providers: [Foo],})class FooModule { }
const container = new Container();
(async () => { await container.addModule(FooModule); await container.resolve<Foo>(Foo); // Foo object})();
#
2. Imports and exportsExports and imports will help you create a hierarchy of module:
exports
: allow you to declare service/module that can be imported from other module.imports
: allow you to import exported service/module.
import { Module, Injectable, Container } from '@cellularjs/di';
// fooclass Foo { run = () => 'foo';}
@Module({ exports: [Foo],})class FooModule { }
// barclass Bar { run = () => 'bar';}
@Module({ exports: [Bar],})class BarModule { }
// foobar@Injectable()class FooBar { constructor( private foo: Foo, private bar: Bar, ) {}
run = () => this.foo.run() + this.bar.run();}
@Module({ imports: [FooModule,BarModule], exports: [FooBar],})class FooBarModule { }
(async () => { const container = new Container(); await container.addModule(FooBarModule);
const fooBar = await container.resolve<FooBar>(FooBar); // const foo = await container.resolve<Foo>(Foo); // Error, there is no provider for "Foo". fooBar.run(); // 'foobar'})();
#
3. Extend moduleextModule
allow you to override exist module.
@Injectable()class Connection { constructor( @Inject('user') private user: string, @Inject('pwd') private pwd: string, ) { }}
@Module({ exports: [Connection],})class DatatableModule { static config(user, pwd) { return { extModule: DatatableModule, providers: [ { token: 'user', useValue: user }, { token: 'pwd', useValue: pwd }, ], } }}
(async () => { await container.addModule(DatatableModule.config('foo', '****X****'));
await container.resolve<Connection>(Connection); // Connection object})();
#
4. Module encapsulationIn CellularJS, module encapsulation look a bit like thing in a class. For example, exports
is similar to "public", so you don't need to declare it in providers
or imports
again.
Example 1: Export a service class without define its provider.
import { Module, Container, Injectable, Inject } from '@cellularjs/di';
class Foo { constructor( @Inject('key') public key: string, ) {}}
@Module({ providers: [ { token: 'key', useValue: 'value' }, ], exports: [Foo],})class FooModule { }
(async () => { const container = new Container(); await container.addModule(FooModule);
const foo = await container.resolve(Foo); console.log(foo.key); // 'value'})();
Example 2: Export a service class and also define its provider. This provider will provide instruction for how to create real value.
import { Module, Container, Injectable, Inject } from '@cellularjs/di';
class Foo { }
class Bar { constructor( @Inject('key') public key: string, ) {}}
@Module({ providers: [ { token: Foo, useClass: Bar }, { token: 'key', useValue: 'value' }, ], exports: [Foo],})class FooModule { }
(async () => { const container = new Container(); await container.addModule(FooModule);
const foo = await container.resolve(Foo); // instance of Bar console.log(foo.key); // 'value'})();
#
5. Module lifecycle#
5.1. onInitModule is initializing when addModule
is called. onInit
event will be triggerd after providers, child modules is added. You can use onInit
method inside module class to listen to this event.
import { Module, OnInit, ModuleRef } from '@cellularjs/di';
@Module({})class YourModule implements OnInit { constructor( // you can access current initialized module(container) with ModuleRef token. private moduleRef: ModuleRef, ) { }
async onInit() { // await this.moduleRef.addProvider(...) console.log('Module initialized!!!'); }}
note
For now, ModuleRef
is only available inside module class.