使用@ngx-formly v5整合自定义Radio组件遇值访问器错误
问题:自定义Radio组件结合@ngx-formly v5时的表单关联错误
自定义Radio组件常规用法
<custom-radio name="regular" value="1" label="Regular" > </custom-radio>
渲染后的简化HTML:
<custom-radio> <label> <input type="radio"> <span class="faux-radio"> Regular </span> </label> </custom-radio>
Formly配置
Formly模块
FormlyModule.forRoot({ types: [ { name: "custom-radio", component: FormlyCustomRadio, // 自定义的Formly包装组件 } ], }),
包装组件代码
@Component({ selector: 'formly-custom-radio', template: ` <custom-radio *ngFor="let option of to.options" [formControl]="formControl" [formlyAttributes]="field" [name]="to.name" [value]="option.key" [label]="option.label" > </custom-radio> ` }) export class FormlyCustomRadio extends FieldType<FieldTypeConfig> {}
Formly字段配置
const fields: FormlyFieldConfig[] = [ { key: "dog", type: "custom-radio", templateOptions: { label: "你养狗狗了吗?", name: "dog", options: [ { key: "1", label: "是" }, { key: "2", label: "否" }, ], }, }, ]
报错信息
Error: No value accessor for form control with unspecified name attribute
解决方案
报错核心是自定义custom-radio组件未实现Angular的ControlValueAccessor接口,导致表单无法识别其作为控件的价值访问逻辑。以下是两种无需重写原组件样式/逻辑的方案:
方案1:用指令实现ControlValueAccessor(推荐)
创建一个指令,在不修改原组件的前提下,为custom-radio添加表单交互能力:
import { Directive, HostListener, ElementRef, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Directive({ selector: 'custom-radio', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CustomRadioValueAccessor), multi: true } ] }) export class CustomRadioValueAccessor implements ControlValueAccessor { private onChange: (value: any) => void = () => {}; private onTouched: () => void = () => {}; constructor(private el: ElementRef) {} private get nativeRadio(): HTMLInputElement { return this.el.nativeElement.querySelector('input[type="radio"]'); } @HostListener('change', ['$event.target.checked']) onRadioChange(checked: boolean) { if (checked) { this.onChange(this.nativeRadio.value); } this.onTouched(); } writeValue(value: any): void { if (this.nativeRadio) { this.nativeRadio.checked = this.nativeRadio.value === value; } } registerOnChange(fn: (value: any) => void): void { this.onChange = fn; } registerOnTouched(fn: () => void): void { this.onTouched = fn; } setDisabledState(isDisabled: boolean): void { if (this.nativeRadio) { this.nativeRadio.disabled = isDisabled; } } }
在模块中声明该指令后,custom-radio即可被Angular表单正常识别。
方案2:手动同步Formly包装组件的表单状态
修改包装组件的模板和逻辑,手动同步单选框状态与表单控件:
@Component({ selector: 'formly-custom-radio', template: ` <custom-radio *ngFor="let option of to.options" [name]="to.name" [value]="option.key" [label]="option.label" (change)="onRadioChange(option.key)" [checked]="formControl.value === option.key" > </custom-radio> ` }) export class FormlyCustomRadio extends FieldType<FieldTypeConfig> { onRadioChange(value: any) { this.formControl.setValue(value); this.formControl.markAsTouched(); } }
若原custom-radio未暴露change事件和checked属性,可通过ElementRef获取内部原生radio的状态进行同步。
内容的提问来源于stack exchange,提问作者Erik
相关产品推荐
相关产品推荐

