I have 3 components which do almost the same things and I want to extract their logic into a shared service. But it requires a string "type" which defines the url that will be used later on. My idea was to pass the type string as a constructor parameter in my component's providers:
@Component({
selector: 'hello',
template: `<h1>Hello {{name}}!</h1>`,
providers: [
{
provide: HelperService,
useFactory: (type: string, termService: TermService) =>
new HelperService(type, termService),
deps: ['userTerms', HelperService],
},
],
})
But that leads to an error:
ERROR Error: R3InjectorError(AppModule)[userTerms -> userTerms -> userTerms]: NullInjectorError: No provider for userTerms!
Here is an example project: https://stackblitz.com/edit/angular-ivy-gyfpvy?file=src/app/helper.service.ts,src/app/term.service.ts,src/app/hello.component.ts
Is there a simple way to achieve this and pass a string?
CodePudding user response:
Besides the option mentioned in the comments that you just include the string in the factory you can also do this using an InjectionToken. You define an InjectionToken for the string value and inject it in the constructor of the service. For example:
export const HELPER_TYPE = new InjectionToken<string>('Helper service type');
@Injectable()
export class HelperService {
constructor(
@Inject(HELPER_TYPE) private readonly type: string,
private readonly termService: TermService
) {}
}
This the simplifies the providers in the component because you don´t need to use a factory.
@Component({
selector: 'hello',
template: `<h1>Hello {{name}}!</h1>`,
providers: [
TermService,
{ provide: HELPER_TYPE, useValue: 'userTerms' },
HelperService
],
})
export class HelloComponent {
@Input() name: string;
constructor(helper: HelperService) {}
}
Here is also a link to a fork of your StackBlitz implementing this solution.