Recently, Angular put in place a migration to replace constructor-based injection with inject function.
In most of the cases, it is straightforward and it clearly has benefits:
// 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);
However, can inject function cover all the constructor-based injection usages?
In particular with useFactory + deps?
To illustrate, I have this 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],
},
...,
],
};
Is there a way to use inject & useFactory + deps together
so that I can replace the constructor declaration in my PrintService with inject calls?
As mentioned by Matthieu Riegler:
if you only rely on inject() you can remove the experimentalDecorators option from your TS config.
Yes, we can use them interchangeably.
Some scenarios will have injection decorators like skipSelf, Optional, Self, Host which can be provided as a second argument to an object containing these properties.
fooPrintService = inject(FOO_PRINT_SERVICE, {
host: false,
optional: false,
self: false,
skipSelf: false,
});
From your question perspective, all you need to do is pass the injection token to the inject method and it can detect it in DI tree.
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'),
},
],
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With