Angular 2+ 材料 mat-chip-list formArray 验证

Posted

技术标签:

【中文标题】Angular 2+ 材料 mat-chip-list formArray 验证【英文标题】:Angular 2+ material mat-chip-list formArray validation 【发布时间】:2019-04-28 05:29:15 【问题描述】:

如何验证 mat-chip 是否已添加到 mat-chip-list。我正在使用 ReactiveForms。我尝试过使用required 验证器。

该值可以是姓名列表,因此我需要确保我的姓名列表中至少有 1 个姓名,然后才能提交表单。如果列表为空,则mat-error 应显示错误消息。使用required 验证器会使表单无效,无论是否将名称添加到列表中。

编辑:反应式表单

我试图制作一个自定义验证器,我现在使用的是响应式表单而不是模板驱动的表单,但我无法让它工作。我编辑了下面的代码以反映我的更改,并创建了这个https://stackblitz.com/edit/angular-4d5vfj

HTML

<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 &gt; 0,如果这对你的场景来说足够了,最简单的方法 这只会隐藏 mat-error,它不会使 formControl 有效。我需要对其进行验证。 是的,但是您可以使用该条件编写自己的验证器。看这里:medium.com/@tarik.nzl/… 我试图制作一个自定义验证器,我现在使用的是响应式表单而不是模板驱动的表单,但我无法让它工作。请参阅编辑后的帖子。我创建了这个stackblitz 自我编辑后没有 cmets。没有解决方案吗?我花了很多时间试图弄清楚这一点,但我被困住了。 【参考方案1】:

问题在于,当chipList 的FormArray 状态为INVALID 时,chipList 的errorState 未设置为true

我也面临同样的问题,不知道为什么这不是开箱即用的,也不知道如何在chipList 的形式为FormArray 的情况下隐式实现这一点。

作为一种解决方法,您可以从FormArray 监听状态变化并手动设置chipList 的errorState

@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】:

不幸的是,不能使用任何 Angular 的预定义验证器,因为它们不是为使用数组而设计的。在这篇文章的帮助下,我设法做到了:

https://www.dev6.com/Angular_Material_Chips_with_Reactive_Forms_and_Custom_Validation

【讨论】:

感谢您的链接。你有工作样本(plunker 或 stackblitz)吗?该博客的作者没有给出任何内容,并且我很确定他的演示中的 html 部分是不完整的。【参考方案3】:

为了能够在mat-chip-list 上进行验证,我们必须将mat-inputmat-chip-list 与相同的FormControl 绑定,如下所示

工作 Stackblitz 链接here

<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>

【讨论】:

【参考方案4】:

正如this example mmalerba 所证明的那样, 您可以使用两个单独的表单控件:一个用于 mat-chip-list(这个应该是带有所需标志的那个),另一个用于输入。有趣的是,您必须在 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 example

【讨论】:

【参考方案5】:

因为有人正面临这个问题。几个小时后,我找到了问题的根本原因和解决方案:

在chiplist的add、remove、select函数中,需要在reactive form中使用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

【讨论】:

以上是关于Angular 2+ 材料 mat-chip-list formArray 验证的主要内容,如果未能解决你的问题,请参考以下文章

Angular 材料 2:如何区分一个组件中的 2 个或多个可排序表?

Angular 材料 2 在组件之间共享反应形式

angular 2 dart中是不是有材料输入类型号

Angular 4 - 材料 2 - md datepicker 设置一周的第一天

Angular 2材料垫选择以编程方式打开/关闭

带有 [ngmodel] 的 Angular 材料 2 日期选择器