如何为Angular信号表单实现动态Schema?
我为简化表单管理编写了如下Angular表单组件:
export type MyFormModel = Record<string, number|string>; export type MyFormFields = MyFormField[]; export type MyFormField = MyFormFieldRange | MyFormFieldText; export interface MyFormFieldRange { type: "range"; key: string; min: number; max: number; } export interface MyFormFieldText { type: "text"; key: string; required?: boolean; } export class MyForm { fields = input.required<MyFormFields>(); model = model<MyFormModel>({}); form = form(this.model); }
我需要根据传入的fields动态生成表单Schema,但尝试多种方式均报错:
- 用
computed信号初始化form时,触发错误:
_effect-chunk2.mjs:2601 ERROR RuntimeError: NG0203: The
_Injectortoken injection failed.inject()function must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used withrunInInjectionContext.
在
ngOnInit或ngOnChanges中延迟初始化form,同样出现上述错误。在Schema函数中直接使用
fields,出现错误:
ERROR RuntimeError: NG0950: Input "fields" is required but no value is available yet.
解决方法
1. 用runInInjectionContext包裹表单生成逻辑
computed和生命周期钩子不在默认注入上下文内,需要手动注入Injector并调用runInInjectionContext来创建表单:
import { inject, runInInjectionContext, input, model, form } from '@angular/core'; export class MyForm { private injector = inject(Injector); fields = input.required<MyFormFields>(); model = model<MyFormModel>({}); form = computed(() => { return runInInjectionContext(this.injector, () => { // 根据fields生成表单控件配置 const controlConfig: Record<string, any> = {}; this.fields().forEach(field => { switch(field.type) { case 'range': controlConfig[field.key] = form.control(0, { min: field.min, max: field.max }); break; case 'text': controlConfig[field.key] = form.control('', field.required ? { required: true } : {}); break; } }); return form.group(controlConfig); }); }); }
2. 依赖computed的信号追踪确保fields就绪
computed会自动监听fields信号的变化,只有当fields()有有效值时才会执行内部逻辑,从根本上避免NG0950错误。
3. 禁止在生命周期钩子中直接创建表单
Angular响应式表单API(如form.group、form.control)必须在注入上下文内调用,所以不要在ngOnInit等钩子中直接初始化表单,改用computed配合runInInjectionContext实现动态生成。
内容的提问来源于stack exchange,提问作者LoganMzz

