验证以防止角度形式出现重复的形式值

Inc*_*ion 2 javascript forms validation angular formarray

我有几个表单数组,需要进行验证,以便每个表单行中的特定字段在所有表单数组中必须是唯一的。如果任何值出现多次,则两个表单字段都必须标记为红色。

我设法编写了一个函数,以便如果这些字段有任何更改,该函数将返回 true/false。但我不确定如何将其用于实际验证过程。

组件.html:

<div formArrayName="temperatureFormArr">
  <div class="row" *ngFor="let temperature of parentForm['controls'].detailForm['controls'].temperatureFormArr['controls']; let i=index"
    [formGroupName]="i">
    <div class="col-4">
      <mat-form-field appearance="outline" class="fex-input">
          <mat-label>Higher Level Function</mat-label>
        <input autocomplete = "off" matInput placeholder="Higher Level Function" (input)="testFunction(i, 'temperatureFormArr')" formControlName="higherLevelFunction">
      </mat-form-field>
    </div>
    <div class="col-6">
      <mat-form-field appearance="outline" class="fex-input">
          <mat-label>Description</mat-label>
        <input autocomplete = "off" matInput placeholder="Description" formControlName="description">
      </mat-form-field>
    </div>
  </div>
</div>

<div formArrayName="waterPressureFormArr">
  <div class="row" *ngFor="let waterPressure of parentForm['controls'].detailForm['controls'].waterPressureFormArr['controls']; let i=index"
    [formGroupName]="i">
    <div class="col-4">
      <mat-form-field appearance="outline" class="fex-input">
          <mat-label>Higher Level Function</mat-label>
        <input autocomplete = "off" matInput placeholder="Higher Level Function" (input)="testFunction(i, 'waterPressureFormArr')"  formControlName="higherLevelFunction">
      </mat-form-field>
    </div>
    <div class="col-6">
      <mat-form-field appearance="outline" class="fex-input">
          <mat-label>Description</mat-label>
        <input autocomplete = "off" matInput placeholder="Description" formControlName="description">
      </mat-form-field>
    </div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

组件.ts

  testFunction(i: any, typeOfArray: string) {
    var duplicateFlag = false;
    var testValue = this.parentForm['controls'].detailForm['controls'][typeOfArray].controls[i].value;
    for (let index = 0; index < this.parentForm['controls'].detailForm['controls'].temperatureFormArr.length; index++) {
      if(this.parentForm['controls'].detailForm['controls'].temperatureFormArr['controls'][index]
      .get('higherLevelFunction').value == testValue.higherLevelFunction && (index != i || typeOfArray != 'temperatureFormArr')) {
        duplicateFlag = true;
      }
    }
    for (let index = 0; index < this.parentForm['controls'].detailForm['controls'].waterPressureFormArr.length; index++) {    
      if(this.parentForm['controls'].detailForm['controls'].waterPressureFormArr['controls'][index]
      .get('higherLevelFunction').value == testValue.higherLevelFunction && (index != i || typeOfArray != 'waterPressureFormArr')) {
        duplicateFlag = true;
      }
    }
    return duplicateFlag;
  }
Run Code Online (Sandbox Code Playgroud)

对于每次击键,都会调用此函数,并对所有其他表单数组中的所有值进行检查,以查看是否存在重复项。但是我如何更改它以便我可以使用此函数作为验证器并相应地标记重复的输入字段?

Eli*_*seo 5

使用验证时有两种方法

1.-验证控件并在(输入)中调用其余控件的 updateValueAndValidity。这是因为当您对控件进行验证时,仅验证该控件。

您可以使用类似的函数

  duplicateControlError(field) {
    return (control: FormControl) => {
      let result: boolean = false;
      const group = control.parent as FormGroup;
      if (group) {
        const values = control.parent.parent.value.map(x => x[field]);
        result = values.filter(x => x == control.value).length > 1;
      }
      return result ? { error: "duplicate" } : null;
    };
  }
Run Code Online (Sandbox Code Playgroud)

另一个更新ValueAndValidity其他控件

  updateValidation(arrayName,field)
  {
    (this.form.get(arrayName) as FormArray).controls.forEach(
      group=>group.get(field).updateValueAndValidity()
    )
  }
Run Code Online (Sandbox Code Playgroud)

.html 变得像

        <mat-form-field class="example-full-width">
            <input matInput placeholder="higherLevelFunction" formControlName="higherLevelFunction" (input)="updateValidation('temperatureFormArray','higherLevelFunction')">
<mat-error>Duplicate</mat-error>
</mat-form-field>
Run Code Online (Sandbox Code Playgroud)

然后你创建这样的表格

  form = new FormGroup({
    temperatureFormArray: new FormArray(
      this.data.map(
        (x, index) =>
          new FormGroup({
            description: new FormControl(x.description),
            higherLevelFunction: new FormControl(
              x.higherLevelFunction,
              this.duplicateControlError("higherLevelFunction")
            )
          })
      )
    )
  });
Run Code Online (Sandbox Code Playgroud)

另一种方法是在 FormArray 上创建自定义验证器

  duplicateError(field) {
    return (formArray: FormArray) => {
      let duplicate = [];
      formArray.value.forEach((x, index) => {
        if (formArray.value.filter(y => y[field] == x[field]).length > 1)
          duplicate.push(index);
      });
      return duplicate.length ? { error: duplicate } : null;
    };
  }
Run Code Online (Sandbox Code Playgroud)

您创建的 formArray 如下

form = new FormGroup({
    temperatureFormArray: new FormArray(
      this.data.map(
        (x, index) =>
          new FormGroup({
            description: new FormControl(x.description),
            higherLevelFunction: new FormControl(
              x.higherLevelFunction)
            )
          })
      ),
      this.duplicateError("higherLevelFunction")
    )
  });
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于 FormArray出现了错误。如果只有正常的输入,我们可以使用类似的

    <div *ngIf="form.get('temperatureFormArray').errors?.error.indexOf(i)>=0">
      duplicate
    </div>
Run Code Online (Sandbox Code Playgroud)

但我们使用的是材料输入,因此当您将控件标记为无效时,我们需要进行更改。为此,我们需要使用自定义 ErrorStateMatcher。这只是一个使 mat-error 显示函数是否返回 true 的函数。我们需要传递 formArrayName 和索引作为参数,所以我们定义了一些类似的

export class DuplicateStateMatcher implements ErrorStateMatcher {
  constructor(private formArrayName:string,private index:number){}
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const formArray=form.form.get(this.formArrayName) as FormArray
    const error=formArray && formArray.errors?
    formArray.errors.error.indexOf(this.index)>=0:null
    return (control && error && (control.dirty || control.touched));
  }
}
Run Code Online (Sandbox Code Playgroud)

我们的组件中有一个函数,例如

  matcher(formArrayName,index)
  {
     return new DuplicateStateMatcher(formArrayName,index);
  }  
Run Code Online (Sandbox Code Playgroud)

.html

        <mat-form-field class="example-full-width">
            <input matInput placeholder="higherLevelFunction" formControlName="higherLevelFunction" [errorStateMatcher]="matcher('temperatureFormArray',i)">
<mat-error>Duplicate</mat-error>
</mat-form-field>
Run Code Online (Sandbox Code Playgroud)

你可以在stackblitz中看到这两种方法