注入TranslocoDecimalPipe到自定义管道出现ChangeDetectorRef报错如何解决
你遇到的报错本质原因是:TranslocoDecimalPipe 依赖Angular的ChangeDetectorRef(CDR),而CDR仅在组件/指令这类和视图关联的注入器上下文下才会被提供,ngrx effect运行在全局根注入器上下文,不存在CDR实例,因此注入时直接抛出空指针错误。导入CommonModule也无法解决问题,因为CommonModule提供的CDR仅作用于当前模块下的组件视图上下文,对effect级别的注入无效。
以下方案优先级从高到低排列:
方案1:拆分逻辑,把格式化能力抽为独立服务(推荐)
该方案可彻底规避上下文依赖问题,同时保持逻辑复用性。把当前管道内的格式化逻辑抽成一个可注入的服务,管道本身仅做薄封装调用服务方法,effect里直接注入该服务使用即可。
示例代码:
首先定义通用格式化服务:@Injectable({ providedIn: 'root' }) export class ValueCurrencyFormatter { constructor(private readonly localeService: TranslocoLocaleService) {} format(value: number | Unset, currency?: string | Unset) { if (isNotSet(value)) { return '-'; } const transformed = this.localeService.formatNumber(value, { minimumFractionDigits: 2, maximumFractionDigits: 3, }); return `${defaultTo('', currency)} ${transformed}`.trim(); } }改造现有自定义管道:
@Pipe({ name: 'valueCurrency' }) export class ValueCurrencyPipe implements PipeTransform { constructor(private readonly formatter: ValueCurrencyFormatter) {} transform(value: number | Unset, currency?: string | Unset) { return this.formatter.format(value, currency); } }之后在ngrx effect中直接注入
ValueCurrencyFormatter调用format方法即可,不会触发任何CDR相关的问题。方案2:直接在effect中使用Transloco底层格式化服务
如果你不想改动现有管道代码,也可以直接在effect里注入TranslocoLocaleService,调用它的formatNumber方法实现和TranslocoDecimalPipe完全一致的格式化效果,手动拼接货币符号即可,不需要用到自定义管道。方案3:手动实例化管道(仅做临时兼容用)
如果你必须在effect里使用管道实例,可以手动传入依赖绕开注入的CDR问题。TranslocoDecimalPipe的构造函数参数为(cfr: ChangeDetectorRef | null, service: TranslocoLocaleService, defaultConfig: TranslocoDecimalPipeConfig),不需要响应式语言切换能力的情况下可以直接传null作为CDR,不影响单次格式化调用。
示例代码:// effect类构造函数内手动实例化 constructor( private readonly localeService: TranslocoLocaleService, @Inject(TRANSLOCO_DECIMAL_PIPE_CONFIG) private readonly decimalConfig ) { this.valueCurrencyPipe = new ValueCurrencyPipe( new TranslocoDecimalPipe(null, this.localeService, this.decimalConfig) ); }该方案缺陷是切换应用语言时,手动实例化的管道不会自动更新格式化规则,仅适合不需要多语言动态切换的场景。
内容的提问来源于stack exchange,提问作者Mike

