Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2+ material mat-chip-list formArray validation

How do I validate that a mat-chip has been added to the mat-chip-list. I am using ReactiveForms. I have tried with the required validator.

The value can be a list of names, so I need to make sure that there is atleast 1 name in my list of names before I can submit the form. If the list is empty then mat-error should display the error message. Using the required validator makes the form invalid, regardless of adding names to the list.

EDIT: Reactive Forms

I have tried to make a custom validator, and I am now using Reactive Forms instead of Template driven forms, but I cannot get it to work. I have edited the below code to reflect my changes and I have created this 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);
  }
}
like image 882
Asgher Qureshi Avatar asked Nov 26 '18 14:11

Asgher Qureshi


2 Answers

The problem is that the chipList's errorState isn't set to true when the chipList's FormArray status is INVALID.

I'm facing the same problem and don't know why this isn't working out of the box or how this can be achieved implicitly with the chipList's form being a FormArray.

As a workaround you can listen to status changes from the FormArray and set the chipList's errorState manually:

@ViewChild('chipList') chipList: MatChipList;

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

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

like image 133
frido Avatar answered Sep 17 '22 22:09

frido


To be able to get the validation working on a mat-chip-list we have to bind both the mat-input and mat-chip-list with same FormControl like below

Working Stackblitz link 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>
like image 39
Dilip Avatar answered Sep 20 '22 22:09

Dilip