You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular通用Material自动完成组件构造函数注入令牌报错问题

问题描述

我实现了一个通用的Material自动完成组件,可适配不同API返回的数据(比如国家、人员、职位等)。这类数据都包含id和name这两个共同属性,所以定义了如下接口:

export interface AutocompleteValue {
    id?: number,
    name: string
}

同时我用统一结构的服务来获取数据,所有服务都继承自同一个泛型基类,都包含getAll()方法:

export class APIService<GetType> {
    getAll(): Observable<T[]> {...};
}

基于上述定义,我开发了一个通用Angular Material自动完成组件:

@Component({...})
export class AutoCompleteInput<Type extends AutocompleteValue, ServiceType extends APIService<Type>> implements ControlValueAccessor, MatFormFieldControl<Type | undefined> {
    ...
    constructor(protected service: ServiceType, ...) {}
    ...
}

在实际使用时,我通过继承这个通用组件实现了职位自动完成组件:

export class PositionsAutoComplete extends AutoCompleteInput<GetDto, PositionService> {
    constructor(
        @Inject(POSITION_SERVICE_TOKEN) protected override service: PositionService, ...){}
}

我定义了注入令牌:

export const POSITION_SERVICE_TOKEN = new InjectionToken<PositionService>('');

并在app.component.ts中配置了提供者:

providers: [{ provide: POSITION_SERVICE_TOKEN, useClass: PositionService }]

目前子类的使用完全正常,但父类AutoCompleteInput的构造函数却报出如下错误:

No suitable injection token for parameter 'service' of class
'AutoCompleteInput'. Consider using the @Inject decorator to specify
an injection token.(-992003)

更新:我之前遇到过类似的通用服务问题,当时只要移除泛型类的@Injectable装饰器就能解决。我觉得这次的问题类似——@Component装饰器会触发泛型类的实例化,导致需要注入令牌,但如果移除@Component装饰器,通用组件的模板(autocomplete-input.component.html)就无法使用了。

请问:为什么父类会出现这个报错?该如何解决?


解决方案

报错原因

Angular的依赖注入系统在处理带@Component装饰器的类时,会尝试解析构造函数中的所有依赖项。你的父类AutoCompleteInput是带有@Component的泛型类,Angular无法推断泛型类型ServiceType对应的注入令牌——因为泛型类型在编译时会被擦除,运行时Angular不知道该注入哪个具体的服务实例,所以抛出了缺少注入令牌的错误。

解决方法

方法一:将父类标记为抽象类

把父类AutoCompleteInput标记为abstract,这样Angular就不会尝试直接实例化它,只会处理作为子类的具体组件。

修改父类定义:

@Component({...})
export abstract class AutoCompleteInput<Type extends AutocompleteValue, ServiceType extends APIService<Type>> implements ControlValueAccessor, MatFormFieldControl<Type | undefined> {
    ...
    constructor(protected service: ServiceType, ...) {}
    ...
}

Angular会识别这是一个抽象基类,不会去解析它的依赖注入,只会在实例化子类时处理依赖——而子类已经通过@Inject指定了具体的注入令牌,所以不会再报错。这是最简单直接的方案,完全适配你现有的子类实现。

方法二:用抽象注入令牌统一父类依赖

如果不想把父类设为抽象类,可以定义一个抽象的注入令牌,让父类依赖这个抽象令牌,再在子类中绑定具体的服务实现。

  1. 定义抽象服务令牌:
export const AUTOCOMPLETE_API_SERVICE = new InjectionToken<APIService<AutocompleteValue>>('Autocomplete API Service');
  1. 修改父类构造函数,使用抽象令牌注入:
@Component({...})
export class AutoCompleteInput<Type extends AutocompleteValue, ServiceType extends APIService<Type>> implements ControlValueAccessor, MatFormFieldControl<Type | undefined> {
    ...
    constructor(
        @Inject(AUTOCOMPLETE_API_SERVICE) protected service: ServiceType,
        ...
    ) {}
    ...
}
  1. 在子类的提供者配置中,将具体服务映射到抽象令牌:
providers: [
  { provide: AUTOCOMPLETE_API_SERVICE, useExisting: POSITION_SERVICE_TOKEN }
]

内容的提问来源于stack exchange,提问作者VPNer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:45:03