9

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.

1 Answer 1

14

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 globally like this:
  //
  //   { provide: BRIDGE_SERVICE_ADAPTER, useClass: BridgeService }
  //
  //   On the above line, notice "useClass" instead of "useExisting"
  constructor(private bridgeService: BridgeService) {
    this.bridgeService.hello();
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I think in the last class you write, in its constructor bridgeService should be typed as BridgeServiceAdapter . Do you think so ?
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.
When using useClass instead of useExisting, my service wasn't singleton even though the service was defined with {providedIn: 'root'}. Not sure what I should expect, but wanted to call it out!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.