题意:Angular4 - 表单控件没有值访问器。
问题背景:
I have a custom element :
我有一个自定义元素:
<div formControlName="surveyType">
<div *ngFor="let type of surveyTypes"
(click)="onSelectType(type)"
[class.selected]="type === selectedType">
<md-icon>{
{ type.icon }}</md-icon>
<span>{
{ type.description }}</span>
</div>
</div>
When I try to add the formControlName, I get an error message:
当我尝试添加 formControlName 时,出现错误消息:
ERROR Error: No value accessor for form control with name: 'surveyType'
I tried to add ngDefaultControl
without success. It seems it's because there is no input/select... and I dont know what to do.
我尝试添加 ngDefaultControl
但没有成功。似乎是因为没有输入框/选择框……我不知道该怎么办。
I would like to bind my click to this formControl in order that when someone clicks on the entire card that would push my 'type' into the formControl. Is it possible?
我想将我的点击事件绑定到这个 formControl,这样当有人点击整个卡片时,可以将我的 'type' 推入 formControl。这可能吗?
问题解决:
You can use formControlName
only on directives which implement ControlValueAccessor.
你只能在实现了 ControlValueAccessor 的指令上使用 formControlName。
Implement the interface 实现接口。
So, in order to do what you want, you have to create a component which implements ControlValueAccessor, which means implementing the following three functions:
所以,为了实现你想要的功能,你需要创建一个实现 ControlValueAccessor 的组件,这意味着要实现以下三个函数:
writeValue
(tells Angular how to write value from model into view) writeValue(告诉 Angular 如何将模型中的值写入视图)registerOnChange
(registers a handler function that is called when the view changes) registerOnChange(注册一个处理函数,当视图变化时调用)registerOnTouched
(registers a handler to be called when the component receives a touch event, useful for knowing if the component has been focused). egisterOnTouched(注册一个处理函数,当组件接收到触摸事件时调用,方便了解组件是否已被聚焦)。
Register a provider 注册一个提供者。
Then, you have to tell Angular that this directive is a ControlValueAccessor
(interface is not gonna cut it since it is stripped from the code when TypeScript is compiled to JavaScript). You do this by registering a provider.
然后,你必须告诉 Angular 这个指令是一个 ControlValueAccessor(接口在 TypeScript 编译为 JavaScript 时会被剥离)。你可以通过注册一个提供者来实现这一点。
The provider should provide NG_VALUE_ACCESSOR and use an existing value. You'll also need a forwardRef here. Note that NG_VALUE_ACCESSOR
should be a multi provider.
提供者应该提供 NG_VALUE_ACCESSOR 并使用现有值。你还需要在这里使用 forwardRef。请注意,NG_VALUE_ACCESSOR 应该是一个多重提供者。
For example, if your custom directive is named MyControlComponent, you should add something along the following lines inside the object passed to @Component
decorator:
例如,如果你的自定义指令名为 MyControlComponent,你应该在传递给 @Component 装饰器的对象中添加以下内容:
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => MyControlComponent),
}
]
Usage
Your component is ready to be used. With template-driven forms, ngModel
binding will now work properly.
你的组件已经准备好使用。使用模板驱动表单时,ngModel 绑定现在将正常工作。
With reactive forms, you can now properly use formControlName
and the form control will behave as expected.
使用响应式表单时,你现在可以正确使用 formControlName,表单控件将按预期行为。
Resources 相关参考资料
- Custom Form Controls in Angular by Thoughtram
- Angular Custom Form Controls with Reactive Forms and NgModel by Cory Rylan