如何使用 angulars controlvalueaccessor 触发表单验证

How to trigger validation on form using angulars controlvalueaccessor

在 angular 中使用表单时,通常的方法是直接在表单中使用表单元素(如输入)

<form>
 <input>
</form>

提交此表单并且输入具有验证器时,例如。必需的。表单输入已经过验证,如果无效,我们可以向用户显示...很好...

为了能够重用自定义输入,我们可以创建一个包含此输入 + 额外内容的组件。

<form>
 <custom-component>
</form>

检查这个 stackblitz:https://stackblitz.com/edit/components-issue-whxtth?file=src%2Fapp%2Fuser%2Fuser.component.html

当点击提交按钮时,只有一个输入被验证。 如果您与两个输入交互,它们将验证

据我所知,在 CVA 组件中没有什么奇怪的。

@Component({
  selector: "user",
  templateUrl: "./user.component.html",
  styleUrls: ["./user.component.scss"],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent implements ControlValueAccessor {
  constructor(@Optional() @Self() public ngControl: NgControl) {
    if (this.ngControl != null) {
      // Setting the value accessor directly (instead of using the providers) to avoid running into a circular import.
      this.ngControl.valueAccessor = this;
    }
  }

  onTouched = (_value?: any) => {};
  onChanged = (_value?: any) => {};

  writeValue(val: string): void {}

  registerOnChange(fn: any): void {
    this.onChanged = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
}

就像我说的,您的输入对验证没有反应的主要原因是您的组件配置为 changeDetection: ChangeDetectionStrategy.OnPush 这将使您的组件仅对受限操作做出反应:

  1. 输入引用发生变化
  2. 事件源自组件或其子组件之一
  3. 在视图中使用异步管道
  4. 运行 显式更改检测(将提供解决方法)

所以你有两个选择:

  1. 采用默认的更改检测策略(就像 Angular Material 所做的那样)
  2. 选择一个丑陋的解决方法。

解决方法如下:

constructor(
    @Optional() @Self() public ngControl: NgControl,
    private cdr: ChangeDetectorRef // import the CDR
  ) {
    if (this.ngControl != null) {
      this.ngControl.valueAccessor = this;
      const { _parent } = this.ngControl as any; // this is not a public property
      // and refers to the parent form
      _parent.ngSubmit.subscribe((r: any) => { // this is fired when the form submitted
        this.cdr.detectChanges(); // detect changes manually
      });
    }
}

Stackblitz

这将适用于您将表单作为父表单的情况。通过一些空检查,它将始终如一地工作。但是你可能会遇到其他一些场景,你可能没有机会触发手动更改检测,这会严重损害你的组件的可重用性。