记录生活中的点滴,分享、学习、创新
在 Angular 自定义表单控件,有时你想要的输入不是标准的文本输入、选择或复选框。通过实现ControlValueAccessor 接口并将组件注册为 NG_VALUE_ACCESSOR,您可以将自定义表单控件无缝地集成到模板驱动或响应表单中,就像它是本地表单一样!
ControlValueAccessor
ControlValueAccessor 是一个接口,充当Angular API 和 DOM 元素之间的桥梁
ControlValueAccessor 是一个连接表单模型和视图(DOM元素)的接口,自定义的表单控件必须实现这个接口,它的作用是:
Angular 引入这个接口的原因是,不同的输入控件数据更新方式是不一样的。例如,对于我们常用的文本输入框来说,我们是设置它的 value 值,而对于复选框 (checkbox) 我们是设置它的 checked 属性。实际上,不同类型的输入控件都有一个 ControlValueAccessor,用来更新视图
Angular 中常见的 ControlValueAccessor 有:
1 2 3 4 5 | export interface ControlValueAccessor { writeValue(obj: any) : void registerOnChange(fn: any) : void registerOnTouched(fn: any) : void } |
writeValue(obj:any)是将表单模型中的值写入视图中。
1 2 3 | writeValue(value: any): void { this ._renderer.setProperty( this ._elementRef.nativeElement, 'value' , value); } |
registerOnChange(fn:any)是一个方法,用于注册在视图中的某些内容发生更改时应调用的处理程序。它获取一个函数,告诉其他表单指令和表单控件更新其值。
1 2 3 | registerOnChange(fn: (_: any) => void): void { this ._onChange = fn; } |
registerOnTouched(fn:any)与registerOnChange()此类似,它专门为控件接收触摸事件时注册一个处理程序。
1 2 3 | registerOnTouched(fn: any): void { this ._onTouched = fn; } |
setDisabledState?(isDisabled: boolean): void; 是一个可选的方法,设置自定义表单的状态
1 2 3 | setDisabledState(isDisabled: boolean): void { this ._renderer.setProperty( this ._elementRef.nativeElement, 'disabled' , isDisabled); } |
AbstractValueAccessor
我们可以把 ControlValueAccessor 中的方法写在一个抽象类中,不同的组件可以实现这个基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | export abstract class AbstractValueAccessor implements ControlValueAccessor { private _value: any = '' ; get value(): any { return this ._value; } set value(v: any) { if (v !== this ._value) { this ._value = v; this .onChange(v); this .onTouched(); } } writeValue(value: any) { this ._value = value; } onChange = (_) => {}; onTouched = () => {}; registerOnChange(fn: (_: any) => void): void { this .onChange = fn; } registerOnTouched(fn: () => void): void { this .onTouched = fn; } } export function MakeProvider(type: any): { provide: any, useExisting: any, multi: boolean} { return { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => type), multi: true }; } |
Example
自定义一个 list 控件,可以选择年级