Search code examples
angulartypescriptdependency-injectiondecoratorinversifyjs

Inversify's dependency injection like angular's using TS decorators


Today I switched a js electron project to typescript, asking myself if there is an equivalence to angular's dependency injection. As Angular Universal seems to be at a very early state and there is no documentation at all in using it with electron instead of express, it seems that inversify could fit my needs. Also because it's more lightweight than angular, as I only need DI.

I ended up in trying to create a decorator that should work similar to NgModule decorator in Angular.

import { app, Menu, ipcMain, BrowserWindow } from 'electron';
import { Container } from 'inversify';
import { DatabaseService } from 'core/database.service';

function servermodule(data: {providers: Array<any>}) {
  // Some code that instantiate the declarations array classes
  // Do they have to be returned, if I don't need a reference?

  let container = new Container();
  return (target: Object) => {
    for(const service of data.providers) {
      container.bind(service).toSelf();
    }
  }

}

which cycles through each entry in providers and binds it to inversifys container object. This function I want to use as follows. The usage would then be exactly as in angulars decorator.

@servermodule({
  declarations: [
    // Some other classes, that maybe can get DatabaseService injected
  ],
  providers: [
    DatabaseService
  ]
})
export class AppModule { ...

The DatabaseService could for example look like

 import { injectable } from 'inversify';

 @injectable()
 export class DatabaseService { ...

And should be injectable in angular style, e.g. like

import { inject } from 'inversify';
import { DatabaseService } from './database.service';

export class Models {
  constructor(@inject(DatabaseService) dbService: DatabaseService) { }
}

I'm not sure about inversify's container. Is its scope only in the decorator function? Would it be better to use it as

container = new Container({ autoBindInjectable: true });

How do I return it properly to the AppModule? Is my idea to declare the classes in servermodule decorator a good thought?

For now I just get the following error message on tsc and electron execution, but there are no ts linting errors.

App threw an error during load
Error: Cannot find module 'database.service'

Another idea/question is: If I want in addition to declarations and providers an import attribute. Would it then be a good idea to modify the constructor by the decorator and import these classes?

Thanks!


Solution

  • Regarding this question half a year ago, I worked on implementing decorator functions for an hierarchical DI that look similar to the angular ones and bundled them in an npm package fl-node-di. With it you are able to generate modules

    FlModule({
      imports: [ AnotherModule ] // imports other FlModules
      providers: [ MyService ] // adds Injectables() to the modules container
      declaration: [] // same as providers but immediatly creates an instance
      exports: [] // places the Injectable() in the parents container
    })
    export class MyModule {}
    

    The other module could look like

    FlModule({
      declarations: [ MyComponent ]
    })
    export class AnotherModule {}
    

    And the component could look like

    @Component()
    export class MyComponent {
      constructor (@Inject(MyService) myService: MyService) {}
    }
    

    Pay attention to the Service. The service is provided in the parent module, so this is hierarchical.