如何在Angular中正确的验证集中获得ControlValueAccessor中的内部控件



我们从Angular(目前版本为12)项目的组件库实现开始。

现在我们将第一个组件实现为蓝图。组件应该封装材料的输入(matInput)标签,mat-errors,一些验证器等等,以便开发人员只需要编写s.th。像

<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 ldemann的post表单验证的启发,使用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网站查看这个关于自定义表单控件的指南。

相关内容

  • 没有找到相关文章