Skip to main content

Module

In essence, Container is a module. Module will help you control the accessibility of provider easier.

1. Providers in module#

import { 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 exports#

Exports 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 module#

extModule 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 encapsulation#

In 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. onInit#

Module 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.