angular - Kan vi ta bort alla konstruktorbaserade injektioner?


Nyligen införde Angular en migration för att ersätta konstruktörsbaserad injektion med inject-funktionen.

I de flesta fall är det enkelt och det har tydligt fördelar:

// before
@Injectable({ providedIn: 'root' })
export class SomeSearchService {
  constructor(
    @Inject(SEARCH_DUE_TIME)
    private readonly dueTime: number,
    private readonly repositoryService: SomeRepositoryService
  ) {}

// after: more concise, better typing
@Injectable({ providedIn: 'root' })
export class SomeSearchService {
  private readonly dueTime = inject(SEARCH_DUE_TIME);
  private readonly repositoryService= inject(SomeRepositoryService);

Kan dock inject-funktionen täcka alla konstruktorbaserade injektionsanvändningar?
Särskilt med useFactory + deps?

För att illustrera har jag denna Stackblitz:

// print.service.ts
@Injectable()
export class PrintService implements IPrintService {
  constructor( // QUESTION: can I replace it with `inject` calls? 
    @Inject(DATA_SERVICE)
    private readonly dataService: IDataService,
    private readonly logger: LoggingService,
    ...
  ) {}

// tokens.ts
export const DATA_SERVICE = new InjectionToken<IDataService>('DATA_SERVICE');
export const FOO_PRINT_SERVICE = new InjectionToken<IPrintService>('FOO_PRINT_SERVICE');
export const BAR_PRINT_SERVICE = new InjectionToken<IPrintService>('BAR_PRINT_SERVICE');

// app.config.ts
export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: FOO_PRINT_SERVICE,
      useFactory: (dataService: IDataService, logger: LoggingService) => new PrintService(dataService, logger),
      deps: [FOO_DATA_SERVICE, LoggingService],
    },
    {
      provide: BAR_PRINT_SERVICE ,
      useFactory: (dataService: IDataService, logger: LoggingService) => new PrintService(dataService, logger),
      deps: [BAR_DATA_SERVICE, LoggingService],
    },
    ...,
  ],
};

Finns det ett sätt att använda inject & useFactory + deps tillsammans
så att jag kan ersätta constructor-deklarationen i min PrintService med inject-anrop?

Svar #1

Som nämnts av Matthieu Riegler:

om du endast förlitar dig på inject() kan du ta bort experimentalDecorators-alternativet från din TS-konfiguration.


Ja, vi kan använda dem omväxlande.

Vissa scenarier kommer att ha injektionsdekorerare som skipSelf, Optional, Self, Host som kan tillhandahållas som ett andra argument till ett objekt som innehåller dessa egenskaper.

fooPrintService = inject(FOO_PRINT_SERVICE, {
  host: false,
  optional: false,
  self: false,
  skipSelf: false,
});

Ur ditt frågeperspektiv behöver du bara skicka injektionstoken till inject-metoden så kan den upptäcka det i DI-trädet.

import { Component, InjectionToken, Injectable, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

export class PrintService {
  constructor(public arg: string) {}
}

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    {{fooPrintService.arg}} | {{barPrintService.arg}}
  `,
})
export class App {
    fooPrintService = inject(FOO_PRINT_SERVICE, {
      host: false,
      optional: false,
      self: false,
      skipSelf: false,
    });
  barPrintService = inject(BAR_PRINT_SERVICE);
  name = 'Angular';
}
export const DATA_SERVICE = new InjectionToken<any>('DATA_SERVICE');
export const FOO_PRINT_SERVICE = new InjectionToken<any>('FOO_PRINT_SERVICE');
export const BAR_PRINT_SERVICE = new InjectionToken<any>('BAR_PRINT_SERVICE');

bootstrapApplication(App, {
  providers: [
    {
      provide: FOO_PRINT_SERVICE,
      useFactory: () => new PrintService('foo'),
    },
    {
      provide: BAR_PRINT_SERVICE,
      useFactory: () => new PrintService('bar'),
    },
  ],
});

Stackblitz-demonstration

Relaterade artiklar