检查 Angular 反应形式的 FormGroups 字段中是否有重复项

Check if there is a duplicate item in FormGroups field in Angular reactive form

在保存我所有的网格行时,我需要检查特定列字段是否存在重复项

 (this.formGroups.get('items') as FormArray).controls.forEach((item) => {
    console.log(item.value.attributeDisplayName);       
  });

attributeDisplayName 有重复值时,我需要通过显示警告或类似内容来阻止提交表单。我如何在当前的 forEach 循环中检查它。谢谢

我在我的 HTML 标记中使用 kendo angular 组件,但我没有使用表单标签。下面是列声明的示例

  <kendo-grid-column field="attributeDisplayName" title="CUSTOM FIELD LABEL" width="190">  
        <ng-template kendoGridEditTemplate let-column="column" let-formGroup="formGroup" >          
          <input ngDefaultControl  class="k-textbox" [formControl]="formGroup.get(column.field)">
          
          <div *ngIf="formGroup.get(column.field).errors && (formGroup.get(column.field).dirty || formGroup.get(column.field).touched)">
                <span style="color:red" class="k-icon k-i-warning"></span>
                <span style="color:red">CUSTOM FIELD LABEL is a required field</span>
                
          </div>

        </ng-template>
    </kendo-grid-column>

下面是 .ts 文件中的 FormGroup 声明

    public formGroup: FormGroup;
    public formGroups: FormGroup = new FormGroup({ items: new FormArray([]) });
    
    public createFormGroup = (dataItem) =>
        new FormGroup({
            atrributeId: new FormControl(dataItem.atrributeId),
            objectType: new FormControl(dataItem.objectType, Validators.required),
            attributeDisplayName: new FormControl(dataItem.attributeDisplayName, Validators.required),     
            dataType: new FormControl(dataItem.dataType, Validators.required),
            inputValues: new FormControl(dataItem.inputValues, [Validators.required,  InputValuesValidator]),
            isGridEligible: new FormControl(dataItem.isGridEligible, Validators.required),
            isInvoiceEligible: new FormControl(dataItem.isInvoiceEligible, Validators.required),
        });

我使用此 FormGroup 的示例函数

    public editRows(grid) {      
      this.isEdited = true;
      let currentRow = 0;
      let rows: any = grid.data.data;
  
      for (let i = 0; i < rows.length; i++) {
        const formGroup = this.createFormGroup(rows[i]);          
        this.formGroup = formGroup;
        (this.formGroups.get('items') as FormArray).push(formGroup);        
        grid.editRow(currentRow, formGroup, {skipFocus: true});
        currentRow++;
      }      
    }

不是在提交时检查它,只是在填写表单时检查它,如果用户填写了重复值,则立即显示错误。

Working demo here.

使用此功能检查重复项 -

checkDuplicacy(event, field: FormControl) {
    let length = this.formGroups.value.items.length;
    let count = 0;
    let controls = (<FormArray>this.formGroups.controls.items).controls;
    for (let i = 0; i < length; i++) {
      if (
        this.formGroups.value.items[i].attributeDisplayName.toLowerCase() ==
        event.target.value.toLowerCase()
      ) {
        count++;
      }
      if (count > 1) {
        field.markAsTouched();
        field.setValidators((f) => <any>{ duplicateName: true });
        field.updateValueAndValidity();
      } else {
        field.clearValidators();
        field.setValidators([Validators.required]);
        field.updateValueAndValidity();
      }
    }
  }

并更新了 HTML -

    <kendo-grid-column field="attributeDisplayName" title="CUSTOM FIELD LABEL" width="190">  
<ng-template kendoGridEditTemplate let-column="column" let-formGroup="formGroup" >          
  <input ngDefaultControl  class="k-textbox" [formControl]="formGroup.get(column.field)" (blur)="checkDuplicacy($event, formGroup.get(column.field))">
  

      <div *ngIf="formGroup.get(column.field).errors && (formGroup.get(column.field).dirty || formGroup.get(column.field).touched)">
            <span style="color:red" class="k-icon k-i-warning"></span>
            <span style="color:red">CUSTOM FIELD LABEL is a required field</span>
            
      </div>
    
      <span *ngIf="formGroup.get(column.field).errors?.duplicateName" style="color:red">
      Duplicate field.</span>
    
    </ng-template>
    </kendo-grid-column>

当然,您可以使用验证器来检查重复项。验证器就像

  validateUniq(index) {
    return (control: AbstractControl) => {
      if (control.value) {

        //search the "formArray"
        const formArray = control.parent
          ? (control.parent.parent as FormArray)
          : null;

        if (formArray) {
          //we create an array with the attributeDisplayNames
          const attributes = formArray.value.map((x) => x.attributeDisplayName);
      
          //only give error if there're duplicate before our
          //control
      
          return attributes.indexOf(control.value)>=0 && 
                 attributes.indexOf(control.value)<index
            ? { duplicateName: true }
            : null;
        }
      }
    };
  }

您可以像这样创建 formGroup - 看看您如何传递“索引”

  initFormField() {
    const index = this.items ? this.items.length : 0;
    return this.fb.group({
      attributeDisplayName: [
        '',
        [Validators.required, this.validateUniq(index)],
      ],
    });
  }

当我们在控件上使用 Validator 时的问题是只有当控件发生变化时才会检查,所以我们需要像@Shyam 说的那样创建一个函数 checkDuplicacy - 但在这种情况下更简单:

  checkDuplicacy(index) {
    this.items.controls.forEach((x,i)=>{
      if (index!=i)
        (x as FormGroup).get('attributeDisplayName').updateValueAndValidity()
    })
  }

完成拼图的最后一块是获取控件的函数

  getAtributeDisplayNameAt(index: number) {
    return this.items
      ? (this.items.at(index).get('attributeDisplayName') as FormControl)
      : null;
  }

还有一个html喜欢

<form [formGroup]="formGroups" novalidate autocomplete="off">
  <div formArrayName="items">
    <div
      *ngFor="let item of items.controls; let index = index"
      [formGroupName]="index"
    >
      <div class="form-control">
        <input
          placeholder="Enter here"
          formControlName="attributeDisplayName" 
          (input)="checkDuplicacy(index)"
        />
        <span
          *ngIf="getAtributeDisplayNameAt(index)?.errors?.duplicateName"
          class="error"
        >
          Duplicate field.</span
        >
      </div>
    </div>
  </div>
  <button (click)="addFormField()">Add More</button>
</form>

看看我们如何在事件 input 中调用函数 checkDuplicacy - 你也可以订阅控件的 valueChange

stackblitz