javascript - Angular DI: Injicerar en tjänst med flera parametervärden


Introduktion

Jag kodar vissa tjänster för ett Angular-projekt, där jag försöker följa goda praxis (såsom SOLID-principerna) för att undvika koppling och bero på abstraktioner.

För en av dessa tjänster har jag definierat följande gränssnitt:

interface BrowserMessageChannel<T> {
  postMessage(message: T): void;
  onMessage$(): Observable<T>;
  close(): void;
}

Denna Browser Message Channel är inte mer än en abstraktion som döljer implementeringsdetaljerna av att ha ett delat meddelandeutrymme som appar öppnade i olika flikar kan använda för att kommunicera med varandra. Detta API kommer också att använda observables så att klienter kan prenumerera på nya meddelanden.

Implementering

Denna tjänst kan implementeras på många sätt, men jag bestämde mig för att använda BroadcastChannel API under ytan:

class BcBrowserMessageChannel<T> implements BrowserMessageChannel<T> {
  private onMessage: Subject<T>;

  constructor(private broadcastChannel: BroadcastChannel) {
    this.setUpOnMessage();
  }

  postMessage(message: T): void {
    this.broadcastChannel.postMessage(message);
  }

  onMessage$(): Observable<T> {
    return this.onMessage;
  }

  close(): void {
    this.broadcastChannel.close();
    this.onMessage.complete();
  }

  private setUpOnMessage(): void {
    this.onMessage = new Subject();

    this.broadcastChannel.onmessage = ({ data }: { data: T }) => {
      this.onMessage.next(data);
    };
  }
}

Och tillhandahåll det så att närhelst en BrowserMessageChannel behövs kommer vi att ha en specifik implementation under huven:

@NgModule({
  providers: [{
    provide: BrowserMessageChannel,
    useClass: BcBrowserMessageChannel
  }]
})
class CommunicationModule {
}

Problem inom Angular-ramverket

Ett BroadcastChannel-objekt måste skickas in i BcBrowserMessageChannel:s konstruktor och ett kanalnamn (sträng) måste också skickas in i BroadcastChannel:s konstruktor. I vanlig TypeScript-kod skulle detta se ut så här:

const bc: BroadCastChannel = new BroadcastChannel(channelName);
const channel: BrowserMessageChannel = new BcBrowserMessageChannel(bc);

Hur kan jag passa denna situation i en Angular-lösning?

Hur man uppnår detta med ramverkets Dependency Injection-mekanism, där följande inte skulle räcka?:

@Component({
  ...
})
class MyComponent {
  constructor(private browserMessageChannel: BrowserMessageChannel) {
    // In my scenario, I would need to specify a channel name to 
    // BrowserMessageChannel, so in this component I can post messages and
    // subscribe to new ones in that particular one
  }
}