Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to setup Angular Material Chip Contol with reactive forms

I have an Angular Reactive Form with Angular Materials

For all my controls I add the required validator.

I'm not sure how to setup the chips control correctly with reactive forms.

Where do you set the formControlName so that the required validator fires? At the moment I have it set on the input field which I'm guessing is wrong.

I just want the courseIds to be a comma seperated string with the course ids.

TS:

form: FormGroup;

ngOnInit() {
    this.form = new FormGroup({
      name: new FormControl("", [Validators.required]),
      courseIds: new FormControl("", Validators.required)
    });
  }

HTML:

  <form [formGroup]="form" (ngSubmit)="submit()">

    <mat-form-field>
      <input matInput type="text" formControlName="name" placeholder="Name">
    </mat-form-field>

    <mat-form-field>
      <mat-chip-list #chipList>
        <mat-chip *ngFor="let cid of courseIds" (removed) = "...">
          {{cid}}
        </mat-chip>

        <input matInput formControlName="courseIds"
               [matChipInputFor]="chipList" 
               placeholder="Ids" 
               (matChipInputTokenEnd)="add($event)">
      </mat-chip-list>
   </mat-form-field>    

....

    <button type="submit">OK</button>
  </form>
like image 497
Sun Avatar asked Jun 07 '19 10:06

Sun


People also ask

How is FormArray used in reactive form?

Binding FormArray to Template We use the formArrayName directive to bind the skills form array to the div element. Now the div and anything inside the div element is bound to the skills form array. Inside the div use ngFor to loop through each element of skills FormArray.


Video Answer


2 Answers

Try setting the formControlName at the <mat-chip-list> level.

In your template, set the ngFor to loop over the courseIds control value

<mat-form-field>
    <mat-chip-list #chipList formControlName="courseIds">
        <mat-chip *ngFor="let cid of form.get('courseIds').value" (removed) = "...">
            {{cid}}
        </mat-chip>

        <input matInput
            [matChipInputFor]="chipList" 
            placeholder="Ids" 
            (matChipInputTokenEnd)="add($event)">
    </mat-chip-list>
</mat-form-field>

Then in your component, create the form group with the initial values for courseIds if any, else use an empty array [] (since the chips display an array and not a string). In your add() and remove() function, add and remove the values from the courseIds control value respectively.

form: FormGroup;

ngOnInit() {
    this.form = new FormGroup({
        name: new FormControl("", [Validators.required]),
        courseIds: new FormControl([], Validators.required)
    });
}

add() {
    ...

    // Add new input to courseIds control value
    this.courseIds.value.push(value);

    this.courseIds.updateValueAndValidity();
}

remove() {
    ...

    // Remove element from control value array
    this.courseIds.value.splice(index, 1);    // where index = index of removed element

    this.courseIds.updateValueAndValidity();
}

// use getter method to access courseIds control value easily
get courseIds() { 
    return this.form.get('courseIds');
}
like image 56
nash11 Avatar answered Oct 19 '22 19:10

nash11


When combining FormArrays with nash11 his answer it won't work. I had the problem that when i edit another formArray in the same form group the changes in the mat-chip list where lost.

Therefore here is my solution how to use material chip control with reactive forms + forms array.

https://stackblitz.com/edit/angular-9gjwo4-h4f9ux?file=app/chips-input-example.ts

chips-input-example.html:

<form [formGroup]="form">

    <mat-form-field class="example-chip-list">
        <mat-chip-list #chipList aria-label="Fruit selection" formArrayName="fruits">
            <mat-chip *ngFor="let fruit of fruitControls.value" [selectable]="selectable" [removable]="removable"
                (removed)="remove(fruit)">
                {{fruit}}
                <mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
            </mat-chip>
            <input placeholder="New fruit..."
           [matChipInputFor]="chipList"
           [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
           [matChipInputAddOnBlur]="addOnBlur"
           (matChipInputTokenEnd)="add($event)">
  </mat-chip-list>
    </mat-form-field>
</form>

chips-input-example.ts

import { COMMA, ENTER } from "@angular/cdk/keycodes";
import { Component } from "@angular/core";
import { MatChipInputEvent } from "@angular/material/chips";
import {
  FormControl,
  FormGroup,
  FormGroupDirective,
  NgForm,
  Validators,
  FormBuilder,
  FormArray
} from "@angular/forms";

/**
 * @title Chips with input
 */
@Component({
  selector: "chips-input-example",
  templateUrl: "chips-input-example.html",
  styleUrls: ["chips-input-example.css"]
})
export class ChipsInputExample {
  visible = true;
  selectable = true;
  removable = true;
  addOnBlur = true;
  readonly separatorKeysCodes: number[] = [ENTER, COMMA];
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      fruits: this.fb.array(["Lemon", "Lime", "Apple"], Validators.required)
    });
  }

  get fruitControls(): FormArray {
    return this.form.controls.fruits as FormArray;
  }

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

    // Add our fruit
    if ((value || "").trim()) {
      this.fruitControls.push(this.fb.control(value));
    }

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

  remove(fruit: string): void {
    const index = this.fruitControls.value.indexOf(fruit);
    if (index >= 0) {
      this.fruitControls.removeAt(index);
    }
  }
}
like image 41
Michael Vonck Avatar answered Oct 19 '22 18:10

Michael Vonck