控制值访问器 (CVA)
ControlValueAccessor 接口正是您所寻找的。p>
为什么?该接口将 DOM 与 Angular 表单分离,允许显示下拉菜单和输入与表单实际使用的值不同。
您可以将自定义输入实现为单独的组件*并传入 FormControl。
以下是未经测试的Working Stackblitz
*Edit 3 - 我相信它也可以作为指令来实现。见-Angular 2 Directive implementing ControlValueAccessor doesn't update 'touched' property on change
在黑匣子之外
您的 app.component.html 最终会看起来像。
<form class="example-form">
<app-auto-special [users]="options" [formControl]="myControl"></app-auto-special>
</form>
app-auto-special 就像一个只关心用户 ID 的黑匣子。
我们可以 patchValue 或 setValue 并且它会做的事情(在内部调用 writeValue)。如果我们与这个组件交互,我们会得到 FormControl 值的用户 ID。
编辑 - 没有什么能阻止你传递整个 User 对象。根据问题,我假设 OP 想要 id。
编辑 2 - 传递用户对象而不是 Working Stackbliz
的示例
黑匣子内部
我们需要使用 NG_VALUE_ACCESSOR 将 app-auto-special 组件注册为 controlValueAccessor 的提供者。这是通过:
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AutoSpecialComponent),
multi: true
}]
所以在黑盒中我们实现了由 4 个方法组成的接口:
writeValue(obj: any): void
registerOnChange(fn: any): void
registerOnTouched(fn: any): void
setDisabledState(isDisabled: boolean)?: void
这通常意味着以下样板:
export class AutoSpecialComponent implements ControlValueAccessor {
public _value: number;
public disabled: boolean;
onChanged: any = () => {};
onTouched: any = () => {};
/*
* ControlValueAccessor boilerplate
*
*/
writeValue(value): void {
this._value = value
}
registerOnChange(fn: any): void {
this.onChanged = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled
}
}
我们复制registerOnChange 和registerOnTouched 提供的函数,并在我们想要更新formControl 值或者它在touched 属性上时调用这些副本(this.onChanged 和this.onTouched)。 setDisabledState 是可选的,writeValue 在初始化时调用,或者当我们从父级调用 patchValue 或 setValue 时调用。
要设置 FormControl,我们调用 this.onChanged(some_value);,我们可以挂钩各种事件 input、focusin、blur、optionSelected,并分别决定发生什么:
- FormControl 值
- 显示哪些选项
- 输入中应包含什么显示字符串
这个答案附带一个警告,这是我完成的第一个 CVA 实现之一,所以我在基础上摇摇欲坠。
额外好处
- 单元测试 - 隔离 DOM 显示与表单
- 逻辑分离 - 父级不再饱和
资源
通过以下 youtube 视频了解更多信息 The Control Value Accessor | Jennifer Wadella
下面附上slides