Angular 2+ Material mat-chip-list FormArray 验证

13
如何验证一个 mat-chip 是否已被添加到 mat-chip-list中。我正在使用 ReactiveForms。我已经尝试使用 required 验证器,但是这使得表单无效,而不管是否向列表中添加了名称。值可以是名称列表,因此在提交表单之前,我需要确保我的名称列表中至少有一个名称。如果列表为空,则应显示错误消息 mat-error。我已尝试创建自定义验证器,并现在使用 Reactive Forms 而不是模板驱动形式,但我无法使其工作。我已编辑以下代码以反映我的更改,并创建了这个链接 https://stackblitz.com/edit/angular-4d5vfj
<form [formGroup]="myForm">
  <mat-form-field class="example-chip-list">
    <mat-chip-list #chipList formArrayName="names">
      <mat-chip *ngFor="let name of myForm.get('names').controls; let i=index;"
        [formGroupName]="i"
        [selectable]="selectable"
        [removable]="removable"
        (removed)="remove(myForm, i)">
        <mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
      </mat-chip>

       <input placeholder="Names"
          [matChipInputFor]="chipList"
          [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
          [matChipInputAddOnBlur]="addOnBlur"
          (matChipInputTokenEnd)="add($event, asset)">
    </mat-chip-list>
    <mat-error>Atleast 1 name need to be added</mat-error>
  </mat-form-field>
</form>

TS

import {COMMA, ENTER} from '@angular/cdk/keycodes';
import {Component} from '@angular/core';
import {FormGroup, FormControl, FormBuilder, FormArray} from '@angular/forms';
import {MatChipInputEvent} from '@angular/material';

@Component({
  selector: 'chip-list-validation-example',
  templateUrl: 'chip-list-validation-example.html',
  styleUrls: ['chip-list-validation-example.css'],
})
export class ChipListValidationExample {
  public myForm: FormGroup;

  // name chips
  visible = true;
  selectable = true;
  removable = true;
  addOnBlur = true;
  readonly separatorKeysCodes: number[] = [ENTER, COMMA];

  // data
  data = {
    names: ['name1', 'name2']
  }

  constructor(private fb: FormBuilder) {
    this.myForm = this.fb.group({
      names: this.fb.array(this.data.names, this.validateArrayNotEmpty)
    });
  }

  initName(name: string): FormControl {
    return this.fb.control(name);
  }

  validateArrayNotEmpty(c: FormControl) {
    if (c.value && c.value.length === 0) {
      return { 
        validateArrayNotEmpty: { valid: false }
      };
    }
    return null;
  }

  add(event: MatChipInputEvent, form: FormGroup): void {
    const input = event.input;
    const value = event.value;

    // Add name
    if ((value || '').trim()) {
      const control = <FormArray>form.get('names');
      control.push(this.initName(value.trim()));
      console.log(control);
    }

    // Reset the input value
    if (input) {
      input.value = '';
    }
  }

  remove(form, index) {
    console.log(form);
    form.get('names').removeAt(index);
  }
}

您可以检查 names.length > 0,如果对您的场景足够简单,那将是最简单的方法。 - Teun van der Wijst
那只会隐藏mat-error,而不会使formControl有效。我需要它被验证。 - Asgher Qureshi
是的,但您可以使用该条件编写自己的验证器。在这里看一下:https://medium.com/@tarik.nzl/angular-2-custom-form-control-with-validation-json-input-2b4cf9bc2d73 - Teun van der Wijst
我尝试制作一个自定义验证器,现在我正在使用响应式表单而不是模板驱动表单,但我无法使其工作。请参见编辑后的帖子。我创建了这个 stackblitz - Asgher Qureshi
自从我的编辑以来,没有任何评论。这个问题没有解决方案吗?我已经花了很多时间尝试解决它,但是我卡住了。 - Asgher Qureshi
5个回答

16

问题在于当chipListFormArray状态为INVALID时,chipListerrorState未设置为true

我遇到了同样的问题,不知道为什么它不能直接使用或如何通过 chipList 表单隐式实现此目标。

作为一种解决方法,您可以监听FormArray的状态更改,并手动设置chipListerrorState

@ViewChild('chipList') chipList: MatChipList;

ngOnInit() {
  this.myForm.get('names').statusChanges.subscribe(
    status => this.chipList.errorState = status === 'INVALID'
  );
}

https://stackblitz.com/edit/angular-4d5vfj-gywxjz


2
谢谢您发布这个。我在这上面花了很多时间。希望像这样的东西有记录在某个地方。 - O.MeeKoh
这并没有解决当芯片为空且表单提交时未更改输入字段状态的初始验证问题。 - user1611728
如果你初始化数据为 [],那么验证将不起作用。 - Fill

5
为了使 mat-chip-list 的验证工作,我们需要将 mat-inputmat-chip-list 绑定到同一个 FormControl 上,代码如下:

可以在 这里 查看可用的 Stackblitz 示例。

<form [formGroup]='group'>
  <mat-form-field class="example-chip-list">
    <mat-chip-list #chipList
                   required
                   formControlName="newFruit">
      <mat-chip *ngFor="let fruit of fruits"
                (removed)="remove(fruit)">
        {{fruit.name}}
        <mat-icon matChipRemove>cancel</mat-icon>
      </mat-chip>
      <input placeholder="New fruit..."
            formControlName="newFruit"
            [matChipInputFor]="chipList"
            [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
            [matChipInputAddOnBlur]="addOnBlur"
            (matChipInputTokenEnd)="add($event)" required>
    </mat-chip-list>
      <!-- add mat-error  -->
    <mat-error *ngIf="group.controls.newFruit.hasError('required')">required!</mat-error>
  </mat-form-field>
</form>


2

谢谢提供链接。你有一个可用的示例(plunker或stackblitz)吗?这篇博客的作者没有提供,我相信他演示的HTML部分是不完整的。 - Marcel Jr. Samson Morasse

0
对于遇到这个问题的人来说,经过多个小时的努力,我已经找到了问题的根本原因和解决方案,如下所示:
在 chiplist 的 add、remove、select 函数中,您需要使用响应式表单中的 setValue 函数才能使验证工作正常。
例如:
  this.form.get('names').value.push('abc');
  this.form.get('names').setValue(this.form.get('names').value); // => this line of code will make the validation work

0

正如this examplemmalerba所展示的一样,您可以使用两个单独的表单控件:一个用于mat-chip-list(这个应该是带有 required 标志的那个),另一个用于输入。有趣的是,在 addEvent 事件中,您将需要手动设置两者的值:

<!-- ... -->
    <mat-chip-list #chipList formControlName="names" required>
    <!-- ... -->
    <input placeholder="Names"
        [formControlName]="name"
        [matChipInputFor]="chipList"
        [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
        [matChipInputAddOnBlur]="addOnBlur"
        (matChipInputTokenEnd)="add($event, asset)">
    </mat-chip-list>
    <!-- ... -->

import {Validators} from '@angular/forms';

    # ...
    constructor(private fb: FormBuilder) {
        this.myForm = this.fb.group({
            names: [this.data.names, Validators.required],
            name: ['']
        });
    }

    # ...
    add(event: MatChipInputEvent, form: FormGroup): void {
      const input = event.input;
      const value = event.value;

      // Add name
      if ((value || '').trim()) {
        const control = <FormArray>form.get('names');
        control.push(this.initName(value.trim()));
        console.log(control);
      }

      // Reset the input value
      if (input) {
        input.value = '';
      }

      // update form control (necessary for correct validation)
      this.myForm.controls.name.setValue(null);
      this.myForm.controls.names.setValue(this.data.names);
    }

    # ...
}

这是他们的stackblitz示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接