我正在尝试在Angular(v5)中创建自定义表单控件。该自定义控件本质上是包装了一个Angular Material组件,但还有一些额外的内容。
我已经阅读了各种实现ControlValueAccessor的教程,但没有找到任何关于编写包装现有组件的组件的内容。
理想情况下,我希望有一个自定义组件来显示Angular Material组件(其中有一些额外的绑定和其他东西),但是能够从父表单传递验证(例如required
),并让Angular Material组件处理它。
示例:
外部组件,包含一个表单,并使用自定义组件
<form [formGroup]="myForm">
<div formArrayName="things">
<div *ngFor="let thing of things; let i = index;">
<app-my-custom-control [formControlName]="i"></app-my-custom-control>
</div>
</div>
</form>
自定义组件模板
本质上,我的自定义表单组件只是将 Angular Material 下拉框与自动完成组合在一起。我可以不创建自定义组件来完成此操作,但以这种方式进行操作似乎更有意义,因为处理过滤等所有代码都可以存在于该组件类中,而不必存在于容器类中(后者不需要关心其实现)。
<mat-form-field>
<input matInput placeholder="Thing" aria-label="Thing" [matAutocomplete]="thingInput">
<mat-autocomplete #thingInput="matAutocomplete">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{ option }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
因此,当 input
更改时,该值应作为表单值使用。
我尝试过的事情
我尝试了几种方法来做到这一点,都有自己的缺陷:
简单的事件绑定
在 input
上绑定 keyup
和 blur
事件,然后通知父项发生更改(即调用 Angular 传递给实现 ControlValueAccessor
的 registerOnChange
中的函数)。
这种方法基本可行,但在从下拉列表中选择一个值时,似乎不会触发更改事件,导致处于不一致状态。
它也没有考虑验证(例如,如果是 "必填",当值未设置时,表单控件将正确无效,但 Angular Material 组件不会显示为无效)。
嵌套表单
这是更接近的方法。我在自定义组件类中创建了一个新表单,其中只有一个控件。在组件模板中,我将该表单控件传递给 Angular Material 组件。在类中,我订阅该控件的 valueChanges
,然后将更改传播回父组件(通过传递给 registerOnChange
的函数)。
这种方法也基本可行,但感觉有点凌乱,应该有更好的方法。
这也意味着,任何应用于我的自定义表单控件(由容器组件)的验证都会被忽略,因为我创建了一个缺少原始验证的新 "内部表单"。
根本不使用 ControlValueAccessor
,而是直接传递表单
就像标题所说...我尝试不按照“正确”的方式进行操作,而是向父表单添加了一个绑定。然后,在自定义组件中创建一个表单控件作为该父表单的一部分。
这对于处理值更新以及在一定程度上进行验证(但必须作为组件的一部分而不是父表单创建)有效,但这感觉不对。
摘要
什么是正确处理此问题的方法?感觉我只是在不同的反模式中摸索,但我找不到任何文档表明甚至支持这样做。
Angular Material <- 自定义控件 <- 包装器组件(包含表单)
- Tom Seldon