-1

当以角度处理表单时,通常的方法是将表单元素(如输入)直接放在表单中

<form>
 <input>
</form>

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

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

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

检查这个堆栈闪电战: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;
  }
}
4

1 回答 1

2

就像我说的那样,您的输入对验证没有反应的主要原因是您的组件已配置,因为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
      });
    }
}

堆栈闪电战

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

于 2020-11-05T21:14:50.927 回答