我有一个自定义元素:
<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>
当我尝试添加formControlName时,我收到一条错误消息:
ERROR错误:名称为:的表单控件没有值访问器:'surveyType'
我尝试添加ngDefaultControl
没有成功。似乎是因为没有输入/选择…我不知道该怎么办
我想把我的点击绑定到这个formControl,以便当有人点击整个卡片时,将我的“类型”推到formControl中。有可能吗?
5条答案
按热度按时间moiiocjp1#
您只能在实现
ControlValueAccessor
的指令上使用formControlName
。实现接口
所以,为了做你想做的事情,你必须创建一个实现
ControlValueAccessor
的组件,这意味着实现以下三个函数:writeValue
(告诉Angular如何将值从模型写入视图)registerOnChange
(注册一个在视图改变时调用的处理函数)registerOnTouched
(注册一个当组件接收到触摸事件时要调用的处理程序,用于了解组件是否已被聚焦)。注册provider
然后,你必须告诉Angular这个指令是一个
ControlValueAccessor
(接口不会削减它,因为当TypeScript编译为JavaScript时,它会从代码中剥离出来)。您可以通过注册一个提供商来完成此操作。提供者应该提供
NG_VALUE_ACCESSOR
并使用现有值。你还需要一个forwardRef
。请注意,NG_VALUE_ACCESSOR
应该是multi provider。例如,如果您的自定义指令名为MyControlComponent,则应在传递给
@Component
装饰器的对象中添加以下内容:用法
您的组件已准备好使用。对于模板驱动的表单,
ngModel
绑定现在可以正常工作了。有了reactive forms,您现在可以正确使用
formControlName
,表单控件将按预期运行。资源
nxagd54h2#
您应该在
input
上使用formControlName="surveyType"
,而不是在div
上使用r6hnlfcb3#
这个错误意味着,当你把一个
formControl
放在一个div
上时,Angular不知道该怎么做。要解决这个问题,你有两个选择。1.你把
formControlName
放在一个元素上,这是Angular开箱即用的支持。它们是:input
,textarea
和select
。1.您实现了
ControlValueAccessor
接口。通过这样做,你告诉Angular“如何访问你的控件的值”(因此得名)。或者简单来说:当你把formControlName
放在一个元素上时,该元素自然不会有一个与之关联的值。现在,实现
ControlValueAccessor
接口一开始可能有点令人生畏。特别是因为没有太多好的文档,您需要在代码中添加大量样板文件。让我试着用一些简单的步骤来分解它。将表单控件移动到自己的组件中
为了实现
ControlValueAccessor
,您需要创建一个新的组件(或指令)。将与表单控件相关的代码移到那里。这样,它也很容易重复使用。在组件中已经有一个控件可能是你需要实现ControlValueAccessor
接口的首要原因,因为否则你将无法将自定义组件与Angular表单一起使用。将样板添加到代码中
实现
ControlValueAccessor
接口是相当冗长的,下面是它附带的样板:那么各个部分在做什么呢?
ControlValueAccessor
接口ControlValueAccessor
接口onChange
和onTouch
的方法,这样你就可以调用这些函数了。所以这一点很重要,要理解:您不需要自己实现onChange和onTouch(除了最初的空实现)。你使用(c)所做的唯一一件事就是让Angular将它自己的函数附加到你的类中。为什么?这样你就可以在适当的时候调用Angular提供的onChange
和onTouch
方法。我们会看到这是如何工作在下面。writeValue
方法时看到它是如何工作的。我把它放在这里,这样ControlValueAccessor
上所有必需的属性都实现了,代码仍然可以编译。实现writeValue
writeValue
所做的是,当表单控件在外部被更改时,在自定义组件内部做一些事情。例如,如果您将自定义表单控件组件命名为app-custom-input
,并在父组件中使用它,如下所示:那么只要父组件以某种方式改变了
myFormControl
的值,writeValue
就会被触发。例如,这可能发生在表单初始化(this.form = this.formBuilder.group({myFormControl: ""});
)或表单重置this.form.reset();
时。如果表单控件的值在外部发生了变化,通常要做的是将其写入表示表单控件值的局部变量。例如,如果你的
CustomInputComponent
围绕一个基于文本的表单控件,它可能看起来像这样:在
CustomInputComponent
的html中:您也可以直接将其写入到input元素,如Angular文档中所述。
现在,您已经处理了当外部发生变化时组件内部发生的情况。现在让我们看看另一个方向。当组件内部发生变化时,如何通知外部世界?
调用onChange
下一步是通知父组件
CustomInputComponent
内部的更改。这就是上面(c)中的onChange
和onTouch
函数发挥作用的地方。通过调用这些函数,您可以将组件内部的更改通知外部。为了将值的更改传播到外部,您需要以新值作为参数调用onChange。例如,如果用户在自定义组件的input
字段中键入内容,则使用更新的值调用onChange
:如果你再次检查上面的实现(c),你会看到发生了什么:Angular将自己的实现绑定到
onChange
类属性。该实现需要一个参数,即更新后的控件值。你现在要做的就是调用这个方法,从而让Angular知道这个变化。Angular现在将继续并在外部更改表单值。这是这一切的关键部分。你通过调用onChange
告诉Angular什么时候应该更新表单控件以及更新什么值。您已经为它提供了“访问控件值”的方法。顺便说一下:
onChange
这个名字是我选的。你可以在这里选择任何东西,例如propagateChange
或类似的。无论你如何命名它,它都是一个带一个参数的函数,由Angular提供,并在运行时通过registerOnChange
方法绑定到你的类。调用onTouch
由于表单控件可以被“触摸”,因此您还应该给予Angular理解您的自定义表单控件何时被触摸的方法。您可以通过调用
onTouch
函数来实现这一点。所以对于我们这里的例子,如果你想保持与Angular对开箱即用的表单控件的兼容性,你应该在输入字段模糊时调用onTouch
:同样,
onTouch
是我选择的名称,但它的实际函数是由Angular提供的,它没有参数。这是有道理的,因为你只是让Angular知道,表单控件已经被触摸过了。把一切放在一起
所以当它们都在一起的时候看起来怎么样?它应该看起来像这样:
更多示例
嵌套表单
请注意,控件值访问器不是嵌套表单组的正确工具。对于嵌套的表单组,您可以简单地使用
@Input() subform
。控制值访问器应该 Packagecontrols
,而不是groups
!请参阅此示例如何为嵌套表单使用输入:https://stackblitz.com/edit/angular-nested-forms-input-2来源
50pmv0ei4#
1-打开app.module.ts
2-将ReactiveFormModule、FormsModule添加到@NgModule装饰器下的imports部分。
3-添加FormsModule后,刷新页面,因为formControlName属性应该可以正常工作。
我希望它能帮助
xfb7svmp5#
对我来说,这是由于选择输入控件上的“multiple”属性,因为Angular对这种类型的控件有不同的ValueAccessor。
而里面的模板是这样使用的
更多详情ref Official Docs