14

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);
  }
}
5
  • You could just check if names.length > 0, easiest way if that is sufficient for your scenario Commented Nov 26, 2018 at 14:36
  • That will just hide the mat-error, it will not make the formControl valid. I need it to be validated. Commented Nov 27, 2018 at 6:34
  • Yes but you can write your own validator using that condition. Take a look here: medium.com/@tarik.nzl/… Commented Nov 27, 2018 at 7:51
  • 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. See edited post. I have created this stackblitz Commented Nov 30, 2018 at 11:43
  • No comments since my edit. Is there not a solution to this? I have spent a lot of time trying to figure this out, but I am stuck. Commented Dec 4, 2018 at 8:54

5 Answers 5

16

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

Sign up to request clarification or add additional context in comments.

3 Comments

thanks for posting this. I've spent a good amount of time on this. I wish stuff like this was documented somewhere.
This doesn't address the initial validation when chips are empty and form is submitted without changing the state of the input field.
if you initialize data = [], then validation will not work
5

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>

Comments

2

Unfortunately, it's not possible to use any of Angular's predefined validators because they are not designed to work with arrays. I manage to do it with the help of this article:

https://www.dev6.com/Angular_Material_Chips_with_Reactive_Forms_and_Custom_Validation

1 Comment

Thanks for the link. Do you have a working sample(plunker or stackblitz) ? The author of this blog did not give one and am pretty sure the html part of his demo is incomplete.
1

For somebody is facing with this problem. After many hours, I have found the root cause of the issue and the solution for it as is:

At function add, remove, select of chiplist, you need to use function setValue in reactive form to make the validation work.

For example:

  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

Comments

0

As demonstrated by this example by mmalerba, you can use two separate form controls: one for the mat-chip-list (this one should be the one with the required flag), and a different one for the input. Interestingly, you'll have to manually set the values of both on the 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);
    }

    # ...
}

Here's their stackblitz example

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.