I made a generic material autocomplete, which I would like to use for different API data e.g. countries, people, positions etc. Each of these data share common properties: id, name. Hence, I defined an interface:
export interface AutocompleteValue {
id?: number,
name: string
}
I also use services to fetch this data, each extending the same structure. There is always a getAll() method, which fetches the data. For this I used generic types, which are extended.
export class APIService<GetType> {
getAll(): Observable<T[]> {...};
}
Finally, I have a generic autocomplete angular material component to be used for each of these types.
@Component({...})
export class AutoCompleteInput<Type extends AutocompleteValue, ServiceType extends APIService<Type>> implements ControlValueAccessor, MatFormFieldControl<Type | undefined> {
...
constructor(protected service: ServiceType, ...) {}
...
}
If I want to use it somewhere:
export class PositionsAutoComplete extends AutoCompleteInput<GetDto, PositionService> {
constructor(
@Inject(POSITION_SERVICE_TOKEN) protected override service: PositionService, ...){}
}
It turns out that I need to provide injection tokens... @Inject(POSITION_SERVICE_TOKEN) and export const POSITION_SERVICE_TOKEN = new InjectionToken<PositionService>('');.
And in the app.component.ts:
providers: [{ provide: POSITION_SERVICE_TOKEN, useClass: PositionService }]
This works fine so far. However, AutocompleteInput also required an injection token and I don't know why? I have the following error at the constructor of AutoCompleteInput:
No suitable injection token for parameter 'service' of class 'AutoCompleteInput'. Consider using the @Inject decorator to specify an injection token.(-992003)
Update: I had a similar issue with generic services, where I only had to remove the @Injectable decorator from the generic class to resolve the issue. I think that this is similar and @Component instantiates the generic class, which requires an InjectionToken. If I remove the @Component, I can't use the template in the generic class (autocomplete-input.component.html).
You need to provide injection token because angular doesnt kbow what class it should provide for ServiceType. Do you have one or multiple services extending getAllProvider? If you have one, you can just provide it.
If you have many, then you cant do whatvyou want to do, at least not in this exact way. You will have to provide different in different components, depending what servoce does some component need