如何在不沉默孩子自己的验证器的情况下以角度向子窗体控件添加错误

问题描述

this.form = this.fb.array([
  this.fb.group({ 
    username: [null,Validators.required]
  }),this.fb.group({ 
    username: [null,...
],uniqueUsernameValidator)

const uniqueUsernameValidator = control => {
  // find duplicate
  
  // when flagging the error
  control.get('0.username').setErrors(ifThereIsAnError) // where `ifThereIsAnError` Could be null
}

某种程度上,uniqueUsernameValidator正在使子表单字段指定的required验证静音。如何解决这个问题?

解决方法

尝试以下方法。 See below Link on Stackblitz

const uniqueUsernameValidator = (control: FormArray) => {
  let nonUnique = (control.value.map(({username}) => username) as any[]).filter((v,i,a) => 
    a.indexOf(v) !== i)
    if(nonUnique.length > 0) {
      return {nonUnique}
    }
  return null
}

这将验证用户提供的用户名是否唯一

,

为控件创建自定义验证器时,应实现ValidatorFn interface

interface ValidatorFn {
  (control: AbstractControl): ValidationErrors | null
}

实现可能如下所示:

const uniqueUsernameValidator = control => {
  const ifThereIsAnError = /* your logic .. find duplicate */
  
  if(!ifThereIsAnError) {
    return null
  }

  return { unique: true  } 

}

就您而言,我认为您的意思是setErrors而不是setError?使用AbstractControl的错误设置器时,您将使用setErrors覆盖所有错误。如果您想保留解决方案而不使用ValidatorFn,则应先获取错误,然后再设置新错误:

const uniqueUsernameValidator = control => {
  const errors: ValidationErrors = this.control.errors;
  const keyNameOfYourError = 'unique';

  // reset the last error before revalidating
  if (errors && errors[keyNameOfYourError]) {
    delete errors[keyNameOfYourError];
  }

  // ifThereIsAnError = execute your logic ... find duplicate
  if (ifThereIsAnError) {
    errors[keyNameOfYourError] = true;
    this.control.setErrors(errors);
  }
}

但是我建议您使用实现ValidatorFn

,

我会这样解决:

https://stackblitz.com/edit/form-array-angular-ax9vna?file=src%2Fapp%2Fapp.component.ts

我的提示和技巧

  1. 我喜欢验证在那里显示错误的地方。因此,控件本身正在处理其errors属性。

  2. 确保您的控件知道什么时候更改了

    this.sub = this.usernamesArr.valueChanges.subscribe(_ => updateControls(this.usernamesArr) );

  3. 我们需要访问用户名控件中的formArray-value。通过绑定 this我们将有权使用它。

    uniqueInArray = uniqueInArray.bind(this);

(从stackblitz复制的代码)

html

<div [formGroup]="usernamesArr"
  *ngFor="let item of usernamesArr.controls; let i = index;">
  <div [formGroupName]="i">
    <input formControlName="username" [placeholder]="'username' + i">
    
    <pre style="color: red">
      {{usernamesArr.controls[i].controls.username.errors | json}}
    </pre>

  </div>
</div>

<button (click)="addItem()">Add</button>

ts

import { Component } from "@angular/core";
import {
  FormBuilder,FormGroup,FormArray,FormControl,Validators,AbstractControl
} from "@angular/forms";
import { Subscription } from "rxjs";

const uniqueInArray = function(
  control: AbstractControl
): { uniqueInArray: string } | null {

  const value = control.value;
  const arr = this.usernamesArr.value as { username: string }[];
  if (value == null || value.length === 0 || arr == null || arr.length < 2) {
    return null;
  }

  return arr.map(_ => _.username).filter(_ => _ == value).length > 1
    ? { uniqueInArray: `${value} already exists.` }
    : null;
};

const updateControls = function(control: FormArray): null {

  control.controls.map((c: FormGroup) =>
    c.controls.username.updateValueAndValidity({
      onlySelf: true,emitEvent: false
    })
  );
  return null;
};

@Component({
  selector: "my-app",templateUrl: "./app.component.html",styleUrls: ["./app.component.css"]
})
export class AppComponent {

  sub: Subscription;

  usernamesArr: FormArray;

  uniqueInArray = uniqueInArray.bind(this);

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.usernamesArr = new FormArray([]);

    this.sub = this.usernamesArr.valueChanges.subscribe(_ =>
      updateControls(this.usernamesArr)
    );
  }

  createItem(): AbstractControl {
    return new FormGroup({
      username: new FormControl("",[Validators.required,this.uniqueInArray])
    });
  }

  addItem(): void {
    this.usernamesArr.push(this.createItem());
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}
,

回答更普遍的设置错误问题,而不必消除其他验证程序的错误:

// Add - retaining existing
const errors = {
    ...control.errors,myError: myValue
  };
control.setErrors(errors);

// Remove - only the one we care about
const errors = { ...control.errors };
delete errors.myError;
control.setErrors(Object.keys(errors).length ? errors : null);

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...