错误加上自定义表单控件组件

时间:2019-02-19 23:47:09

标签: angular angular-forms angular-material-6

我正在尝试创建一个自定义MatFormFieldControl,它也实现了ControlValueAccessor。

我想从一个已知的用例开始:密码表单。

计划:创建一个获取单个字符串的表单字段,但其中包含自己的表单,该表单将添加针对confirmPassword字段的验证。

我创建了一个StackBlitz with a working implementation

每次调用errorState变量时,我都会检查两个字段之间是否存在不匹配,并更新ngControl上的错误,以便将其反映到外部。

首先会正确显示错误状态,但是如果您匹配密码并且再次不匹配,则会抛出此错误:

Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: null'. Current value: 'ngIf: [object Object]'.

我在网上找不到很多有关此文档的文档,并且不确定何时将错误添加到错误对象的正确位置

任何人都知道如何避免此错误吗?

预先感谢

get errorState() {
  const missmatch = this.weightForm.value.password !== this.weightForm.value.confirmPassword;

  if (missmatch) {
    this.ngControl.control.setErrors({ missmatch }, { emitEvent: false });
  }

  return (this.ngControl.errors !== null || missmatch) && !!this.ngControl.touched;
}

这里的想法是在外部表单不知道confirmPassword的情况下创建“关注点分离”,因为如果可以的话,它更多的是“ Validation”字段而不是“ data”字段。

关注点分离的另一个示例是将代码编辑器作为表单字段,并使代码编辑器的验证向外部反映,而无需将代码编辑器本身暴露于外部。

2 个答案:

答案 0 :(得分:1)

测试您的StackBlitz示例,我已经看到在mat-error上删除app.component中的* ngIf解决了保持不变的问题 表单输入的“错误报告功能”,我不知道这是否可以解决您实际网站中的问题,但我认为值得尝试。

但是对于这种工作(密码匹配验证),我的真正建议是依靠 Angular表单验证器(此处为表单文档:https://angular.io/guide/form-validation),因为您可以使用这种方法更安全不需要通过变量来强制进行错误检查,而是这种形式可以单独了解字段是否无效。

没有标准的验证器来检查2个字段是否包含相同的文本,但是您可以构建自己的自定义验证器来完成所需的工作。

如果这有帮助,我将分享我通常用于密码检查的自定义验证器的语法(仅作为跟踪,确保您必须进行一些修改才能使其起作用):

export function mismatchValidator(): ValidatorFn {
    return (control: AbstractControl): {[key: string]: any} | null => {
        if (control.parent) {
            if (control.parent.get('password').value !== control.parent.get('password_confirmation').value) {
                return {mismatch: true};
            } else {
                return null;
            }
        } else {
            return null;
        }
    };
}

有关如何实现表单控制器,验证器和自定义验证器的其他信息,可以在Angular网站上找到。

关于您的问题,这里很有趣,article,如果您选择不实施表单验证器,也许可以帮助您了解问题所在。

答案 1 :(得分:1)

我知道这有点晚了,但我在研究另一个问题时偶然发现了这个问题,并认为值得添加一个答案来帮助其他阅读此内容的人。

问题是在 getter 中设置错误的方式。在某个时刻,Angular 将在更改检测循环内读取属性的值,如果此时更改控制错误,您将收到 Expression Changed 错误。

简单的解决方法是稍微改变您测试和设置错误的方式。在您的 stackblitz 中,我将错误状态改回布尔字段,并将验证检查添加到 DoCheck 生命周期挂钩。

  ngDoCheck() {
    const missmatch = this.innerForm.value.password !== this.innerForm.value.confirmPassword;

    if (missmatch) {
      this.ngControl.control.setErrors({ missmatch });
      this.errorState = true;
      return;
    }

    this.errorState = false;
  }

Modified stackblitz with the fix