2

I working with Angular 8.

This question is only for Angular DI experts.

At first, I made an Angular library that will be reused in many project.

In this Library, I made an abstract service, that have to be defined in the project using it.

I defined an abstract class in my Library named BridgeService

and In my main project, I create my BridgeService implementing it: ( in the next lines, I will show you the abstract BridgeService)

MainProject > BridgeService :

import { BridgeService as AbstractBridgeService } from 'myLibrary';

@Injectable()
export class BridgeService implements AbstractBridgeService {

    hello(){
        alert('hello Word');
    }
}

MainProject > UsersModule :

import { UsersModule as LibraryUsersModule } from 'myLibrary';

@NgModule({
    imports: [
        CommonModule,
        LibraryUsersModule,
    ],
    //...
    providers: [
        BridgeService
        // some try:
        //{ provide: BridgeService, useClass: BridgeService }
    ],
})
export class UsersModule {

    constructor(private bridgeService: BridgeService){
        this.bridgeService.hello();
    }

}

And now we have to see the library concerned files:

Library > bridge.service.ts:

export abstract class BridgeService {
    abstract hello(): void;
}

This BridgeService have to be defined in UsersModule

Library > UsersModule:

@NgModule({
    //...
    providers: [
        // My Try:
        { provide: BridgeService, useValue: BridgeService} 
    ],
})
export class UsersModule {

    # my goal is that I can uncomment this piece of code:

    //constructor(private bridgeService: BridgeService){
    //  this.bridgeService.hello();
    //}

}

I want that you touch just two lines in this code:

1) In library's UsersModule providers array: the line of BridgeService that I want to declare as an abstract to be generic.

2) In project's UsersModule providers array: the line of BridgeService that I want to define to take place of the abstract in the Library UserModule.

Med Karim Garali
  • 661
  • 1
  • 8
  • 23

1 Answers1

3

You need to define a token in your library, that's all. In your library, you define the token and the interface:

/** Token to inject the bridge service */
export const BRIDGE_SERVICE_ADAPTER:
    InjectionToken<BridgeServiceAdapter> =
        new InjectionToken<BridgeServiceAdapter>('Bridge service token');

export interface BridgeServiceAdapter {
  hello(): void;
}

Inside your library, you can inject it like this:

export class UsersModule {
  constructor(
    @Inject(BRIDGE_SERVICE_ADAPTER) private bridgeService: BridgeServiceAdapter
  ){
    if(!bridgeService) {
      throw new Error('You must provide a bridge adapter');
    }
    this.bridgeService.hello();
  }
}

And, in the app where you're going to use the library:

import { BridgeServiceAdapter } from 'myLibrary';

@Injectable({providedIn: 'root'})
export class BridgeService implements BridgeServiceAdapter {
    hello() { alert('hello Word') }
}

...
import { UsersModule as LibraryUsersModule, BRIDGE_SERVICE_ADAPTER } from 'myLibrary';

@NgModule({
    imports: [CommonModule,LibraryUsersModule,...],
    providers: [{ provide: BRIDGE_SERVICE_ADAPTER, useExisting: BridgeService }],
})
export class UsersModule {
  // Obs 1: ok, this seems something you should be encapsulating inside
  //   your library to doesn't have to inject in the dependent projects
  // Obs 2: and, here, in the app, as I created BridgeService decorated
  //   with {providedIn: 'root'}, I can inject the service itself. But
  //   if this wasn't the case, I'd have to inject it using the same notation
  //   used in the library: 
  //   @Inject(BRIDGE_SERVICE_ADAPTER) private bridgeService: BridgeServiceAdapter
  //   This would be necessary if I had decorated my service with
  //
  //   @Injectable() (instead of @Injectable({providedIn: 'root'}))
  //
  //   and provided it like this:
  //
  //   { provide: BRIDGE_SERVICE_ADAPTER, useClass: BridgeService }
  //
  //   On the above line, notice "useClass" instead of "useExisting"
  constructor(private bridgeService: BridgeService) {
    this.bridgeService.hello();
  }
}
julianobrasil
  • 6,733
  • 2
  • 22
  • 43
  • I think in the last class you write, in its constructor bridgeService should be typed as BridgeServiceAdapter . Do you think so ? – Med Karim Garali May 01 '20 at 11:37
  • In fact, as I used `{providedIn: 'root'}` in the service decorator, inside my app there's no problem in using the service directly. I think I'm gonna revert my edition because it's a good example of this situation. In the library, there's no option: you must use the `@Inject(BRIGE_SERVICE_ADPATER)` token to inject it. – julianobrasil May 01 '20 at 12:04