简体   繁体   English

如何在 Angular 的正确验证状态中获取 ValueControlAccessor 中的内部控件

[英]How to get the inner control in a ValueControlAccessor in the correct validation satet in Angular

We started with implementation of a component library for our Angular (version 12 at the moment) project.我们开始为我们的 Angular(目前是 12 版)项目实现一个组件库。

At the moment we implement the first component as a blueprint.目前,我们将第一个组件实现为蓝图。 The component should encapsulate a Material input ( matInput ) with label, mat-errors, some validators etc. so that the developer just needs to write s.th.该组件应封装带有标签、mat-errors、一些验证器等的 Material 输入 ( matInput ),以便开发人员只需要编写 s.th。 like喜欢

<our-input max-length="5" required label="fancy field" [formControlName]="fancyField">

to get a fully designed input field.获得一个完全设计的输入字段。 Btw.顺便提一句。 I'm aware that mixing validations from FormControl and template should not be done, but that is only for the sake of the example;我知道不应该混合来自FormControl和模板的验证,但这只是为了示例; in real life the validation will be defined by the component like a datepicker.在现实生活中,验证将由组件(如日期选择器)定义。

At the moment our template is realy simple:目前我们的模板非常简单:

<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>

We are stuck at the part with the validation.我们被困在验证的部分。 If the validation happens only for the "outer control" (the control fancyField in the example) the inner matInput neither get the styling nor not show the validation errors because it simply does not know that it is invalid.如果仅对“外部控件”(示例中的控件fancyField )进行验证,则内部matInput既不会获得样式也不会显示验证错误,因为它根本不知道它是无效的。

Because we register some validators in a similar way Netanel Basal described in his blog post Adding Integrated Validation to Custom Form Controls in Angular we already have access to the outer NgControl :因为我们以与 Netanel Basal 在他的博客文章中将集成验证添加到 Angular 中的自定义表单控件中描述的类似方式注册了一些验证器,所以我们已经可以访问外部NgControl

  constructor(@Self() private ngControl: NgControl) {
    controlDirective.valueAccessor = this;
  } 

We already tried to call the outer validators as an inner validator我们已经尝试将外部验证器称为内部验证器

 this.formControl.addValidators((control) => this.ngControl.control.validator?.(control));
 // the same for asyncValidators?

or inspired by Christian Lüdemann's post Form validation with ControlValueAccessor to assign the outer form control to the inner form control on view-init或受 Christian Lüdemann 的帖子Form validation with ControlValueAccessor的启发,将外部表单控件分配给 view-init 上的内部表单控件

public ngAfterViewInit(): void {
    // syncing with validators on host element
    this.formControl = this.ngControl.control as FormControl;
}

but both methods trigger the validators twice.但是这两种方法都会触发验证器两次。 Additionally I have a strange feeling about connecting a FormControl to two template controls.此外,我对将 FormControl 连接到两个模板控件有一种奇怪的感觉。

Here is an example at StckBlitz https://stackblitz.com/edit/angular-ivy-swiydl这是 StckBlitz 的一个示例https://stackblitz.com/edit/angular-ivy-swiydl

I guess there some other solutions, too.我想还有其他一些解决方案。

I already thought about trying to mirror the outer state but did not find any feasible way to have change detection for properties of an object which is not in a template.我已经考虑过尝试镜像外部状态,但没有找到任何可行的方法来对不在模板中的对象的属性进行更改检测。 The only solution I could found was to create an inner component to bind properties of this.ngControl.control but the felt strange, too.我能找到的唯一解决方案是创建一个内部组件来绑定this.ngControl.control的属性,但感觉也很奇怪。

Which way would you got or is there any better way?您会采用哪种方式或有更好的方式?

Since you are injecting NgControl it's your job to set up the NgControl correctly with the right value accessor and validators.由于您正在注入NgControl ,因此您的工作是使用正确的值访问器和验证器正确设置NgControl You've set up the value accessor correctly.您已正确设置值访问器。 For the latter part you can do something like:对于后一部分,您可以执行以下操作:

ngOnInit(): void {
   const control = this.ngControl.control;
   const validators = control.validator
      ? [control.validator, Validators.required]
      : Validators.required;
   control.setValidators(validators);
   control.updateValueAndValidity();
}

Notice that in this snippet we don't replace validators, instead, we add the required validation to any existing validations.请注意,在此代码段中,我们没有替换验证器,而是将required的验证添加到任何现有的验证中。

Here is an updated StackBlitz demo .这是一个更新的StackBlitz 演示 Have a look at the personal.component.ts .看看personal.component.ts It's a composite CVA, but the logic would be pretty much the same in your case.这是一个复合 CVA,但在您的情况下逻辑几乎相同。

I also wrote this article about custom form components.我还写了这篇关于自定义表单组件的文章。 Although it does not involve a Material input, it may still be helpful to you.尽管它不涉及 Material 输入,但它仍然可能对您有所帮助。

Finally, you can check this guide on custom form controls, from the official Material website.最后,您可以在 Material 官方网站上查看有关自定义表单控件的指南

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM