我们首先为 Angular(目前版本 12)项目实现组件库。
目前我们将第一个组件作为蓝图实现。该组件应该封装一个带有标签、mat-errors、一些验证器等的 Material 输入(
matInput
),以便开发人员只需要编写一些东西。喜欢
<our-input max-length="5" required label="fancy field" [formControlName]="fancyField">
获得完全设计的输入字段。顺便提一句。我知道不应该混合来自
FormControl
和模板的验证,但这只是为了示例;在现实生活中,验证将由组件定义,例如日期选择器。
目前我们的模板非常简单:
<mat-form-field class="example-full-width" appearance="fill">
<input
matInput
[formControl]="formControl"
(input)="onInputChange($event)"
(blur)="onInputBlur()"
/>
<mat-error *ngFor="let error of outerControl.errors | keyvalue">
<ng-container [ngSwitch]="error.key">
<ng-container *ngSwitchCase="'required'">
Field required error
</ng-container>
...
</ng-container>
</mat-error>
</mat-form-field>
我们陷入了验证部分。如果验证仅针对“外部控件”(示例中的控件
fancyField
)进行,则内部 matInput
既不会获取样式,也不会显示验证错误,因为它根本不知道它是无效的。
因为我们以 Netanel Basal 在他的博客文章在 Angular 中向自定义表单控件添加集成验证中描述的类似方式注册了一些验证器,所以我们已经可以访问外部
NgControl
:
constructor(@Self() private ngControl: NgControl) {
controlDirective.valueAccessor = this;
}
我们已经尝试将外部验证器称为内部验证器
this.formControl.addValidators((control) => this.ngControl.control.validator?.(control));
// the same for asyncValidators?
或受到 Christian Lüdemann 的帖子使用 ControlValueAccessor 进行表单验证的启发,将外部表单控件分配给 view-init 上的内部表单控件
public ngAfterViewInit(): void {
// syncing with validators on host element
this.formControl = this.ngControl.control as FormControl;
}
但这两种方法都会触发验证器两次。此外,我对将 FormControl 连接到两个模板控件有一种奇怪的感觉。
这是 StckBlitz 的示例 https://stackblitz.com/edit/angular-ivy-swiydl
我想还有其他一些解决方案。
我已经考虑过尝试镜像外部状态,但没有找到任何可行的方法来对不在模板中的对象的属性进行更改检测。我能找到的唯一解决方案是创建一个内部组件来绑定
this.ngControl.control
的属性,但也感觉很奇怪。
你会选择哪种方式或者有更好的方式吗?
既然您正在注入
NgControl
,那么您的工作就是使用正确的值访问器和验证器正确设置 NgControl
。您已正确设置值访问器。对于后一部分,您可以执行以下操作:
ngOnInit(): void {
const control = this.ngControl.control;
const validators = control.validator
? [control.validator, Validators.required]
: Validators.required;
control.setValidators(validators);
control.updateValueAndValidity();
}
请注意,在此代码片段中,我们不会替换验证器,而是将
required
验证添加到任何现有验证中。
这里是更新的 StackBlitz 演示。看看
personal.component.ts
。这是一个复合 CVA,但逻辑在您的情况下几乎是相同的。
我还写了关于自定义表单组件的这篇文章。虽然不涉及Material输入,但可能对你还是有帮助的。
最后,您可以从官方 Material 网站查看关于自定义表单控件的本指南。