Unikanie kolizji wstrzykiwania: IniectionToken

Angular pozwala czasem na użycie nazw (tokenów) w miejsce identyfikatorów (na przykład: export const identyfikator = 'my-config';). W systemie DI może prowadzić to do konfliktów, gdy użyjemy tokenów dostarczanych przez różne klasy jako wartości provide (zob. poprzedni przykład). Dwa wstrzykiwane obiekty mogą dostarczać taki sam token.

Rozważmy to na przykładzie, w którym główna aplikacja jest konsumentem dwóch modułów: jednego, który zapewnia usługę poczty e-mail, a drugiego zapewniającego usługę rejestrowania.

app/email/email.service.ts

export const apiConfig = 'api-config';

@Injectable()
export class EmailService {
  constructor(@Inject(apiConfig) public apiConfig) { }
}

app/email/email.module.ts

@NgModule({
  providers: [ EmailService ],
})
export class EmailModule { }

Interfejs usługi e-mail wymaga pewnych ustawień konfiguracyjnych, identyfikowanych przez ciąg api-config, który ma być dostarczony przez system DI. Ten moduł powinien być wystarczająco elastyczny, aby mógł być używany przez różne moduły w różnych aplikacjach. Oznacza to, że te ustawienia powinny być określone przez własność aplikacji, a więc dostarczone przez AppModule, gdzie importowany jest EmailModule.

app/logger/logger.service.ts

export const apiConfig = 'api-config';

@Injectable()
export class LoggerService {
  constructor(@Inject(apiConfig) public apiConfig) { }
}

app/logger/logger.module.ts

@NgModule({
  providers: [ LoggerService ],
})
export class LoggerModule { }

Druga usługa, LoggerModule, została stworzona przez inny zespół niż ten, który stworzyłEmailModule, i który również wymaga obiektu konfiguracyjnego. Nic dziwnego, że zdecydowali się użyć tego samego tokena dla swojego obiektu konfiguracyjnego, ciągu api-config. Aby uniknąć kolizji między dwoma tokenami o tej samej nazwie, możemy spróbować zmienić nazwę importu, jak pokazano poniżej.

app/app.module.ts

import { apiConfig as emailApiConfig } from './email/index';
import { apiConfig as loggerApiConfig } from './logger/index';

@NgModule({
  ...
  providers: [
    { provide: emailApiConfig, useValue: { apiKey: 'email-key', context: 'registration' } },
    { provide: loggerApiConfig, useValue: { apiKey: 'logger-key' } },
  ],
  ...
})
export class AppModule { }

Zobacz przykład [022]

Jednak zmieniono tylko nazwę zmiennej zawierającej token, a same tokeny są identyczne. Po uruchomieniu aplikacji DI napotyka na problem kolizji, w wyniku czego oba moduły uzyskują tę samą wartość dla swojej konfiguracji, w tym przypadku{ apiKey: 'logger-key' }.

Rozwiązaniem problemu jest IniectionToken Angulara.

IniectionToken

Tokeny InjectionToken (do wersji <4 OpaqueToken - https://stackoverflow.com/questions/43419050/angular-2-opaquetoken-vs-angular-4-injectiontoken\. są unikalnymi i niezmiennymi wartościami, które pozwalają programistom uniknąć kolizji zależności tokenów iniekcji.

import { InjectionToken } from '@angular/core';

const name = 'token';
const token1 = new InjectionToken(name);
const token2 = new InjectionToken(name);

console.log(token1 === token2); // false

Tutaj, niezależnie od tego, czy ta sama wartość zostanie przekazana do konstruktora tokena, nie spowoduje to uzyskania identycznych symboli (zob. przykład [022]).

app/email/email.module.ts

export const apiConfig = new InjectionToken('api-config');

@Injectable()
export class EmailService {
  constructor(@Inject(apiConfig) public apiConfig: EmailConfig) { }
}
export const apiConfig = new InjectionToken('api-config');

@Injectable()
export class LoggerService {
  constructor(@Inject(apiConfig) public apiConfig: LoggerConfig) { }
}

Zobacz przykład [023]

Po przekształceniu tokenów identyfikujących w InjectionToken bez zmiany czegokolwiek, unika się kolizji.