Home > Enterprise >  Adding drag and drop to mat-chip list within mat-form-field
Adding drag and drop to mat-chip list within mat-form-field

Time:09-30

I am trying to combine this example (A dropdown selection that creates mat-chips of selected values) with a drag and drop functionality borrowed from Angular Material's Mat-Chip examples (second example from the top) but for some reason the drag and drop doesn't work. I can drag a chip and when hovering it over another chip it seems to change index but when I drop it (release mouse button) it goes back to it's original place. Is this a compatibility issue with the Material components? This is my code currently:

HTML:

  <mat-form-field class="field">
  <mat-label>Choose values</mat-label>
  <mat-select [formControl]="toppingsControl" multiple>
    <mat-select-trigger>
      <mat-chip-list
        cdkDropList
        cdkDropListOrientation="horizontal"
        (cdkDropListDropped)="drop($event)"
      >
        <mat-chip
          cdkDrag
          *ngFor="let topping of toppingsControl.value"
          [removable]="true"
          (removed)="onToppingRemoved(topping)"
        >
          {{ topping.name }}
          <mat-icon matChipRemove>cancel</mat-icon>
        </mat-chip>
      </mat-chip-list>
    </mat-select-trigger>

    <mat-option *ngFor="let topping of toppings" [value]="topping">{{
      topping.name
    }}</mat-option>
  </mat-select>
</mat-form-field>

Typescript:

import { Component } from "@angular/core";
import {FormControl} from '@angular/forms';
import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop";

export interface Topping {
  name: string;
}

@Component({
  selector: 'app-control',
  templateUrl: 'control.component.html',
  styleUrls: ['control.component.scss'],
})
export class ControlComponent {
  toppingsControl = new FormControl([]);
  toppings: Topping[] = [
    {name: 'Value_0'},
    {name: 'Value_1'},
    {name: 'Value_2'},
    {name: 'Value_3'},
    {name: 'Value_4'}
  ];

  onToppingRemoved(topping: string) {
    const toppings = this.toppingsControl.value as string[];
    this.removeFirst(toppings, topping);
    this.toppingsControl.setValue(toppings); // To trigger change detection
  }

  private removeFirst<T>(array: T[], toRemove: T): void {
    const index = array.indexOf(toRemove);
    if (index !== -1) {
      array.splice(index, 1);
    }
  }

  drop(event: CdkDragDrop<Topping[]>) {
    moveItemInArray(this.toppings, event.previousIndex, event.currentIndex);

}}

CodePudding user response:

I reproduced your example, saw one mistake

Mistake 1: When you are dragging(here drop function) the Chips only changes occurs in the array parameter but not the formcontroller parameter. If you change the position of controllers then you will get whats required. ie,

drop(event: CdkDragDrop<Topping[]>) {
    moveItemInArray(this.toppings, event.previousIndex, event.currentIndex);

}}

becomes

drop(event: CdkDragDrop<Topping[]>) {     
    moveItemInArray(this.toppings, event.previousIndex, event.currentIndex);
    moveItemInArray(this.toppingsControl.value, event.previousIndex, event.currentIndex);
  }

Hope this will clear your doubts. For further reference go through multi-test of the below example. Here you can do the comparison with the original examples(that you provided).

https://stackblitz.com/edit/angular-material-v9-mat-select-with-mat-chip-list-lcwahb?file=src/app/multi-test-example.ts

  • Related