Ali Turab Abbasi
Ali Turab Abbasi

Reputation: 1133

Angular Drag drop with formArray

I have 2 arrays. I'm implementing an Angular drag drop and I want to use FormArray to save the array in which the element was dropped into.

The problem is that I can't apply formcontrol to the div as it gives an error

Error: No value accessor for form control with name: 'language'

Here is the html

      <div>

        <div class="example-container">
          <h2>Selected Languages</h2>
          <div
          cdkDropList
          #todoList="cdkDropList"
          [cdkDropListData]="anotherarray"
          [cdkDropListConnectedTo]="[doneList]"
          class="example-list"
          (cdkDropListDropped)="drop($event)"
           formControlName="language">
          <div class="list-group-item list-group-item-action " *ngFor="let item of anotherarray" cdkDrag>
            {{item}}
          </div>
        </div>
      </div>

      <div class="example-container">
        <h2>Available Languages</h2>

        <div
        cdkDropList
        #doneList="cdkDropList"
        [cdkDropListData]="testingarray"
        [cdkDropListConnectedTo]="[todoList]"
        class="example-list"
        (cdkDropListDropped)="drop($event)">
        <div class="list-group-item list-group-item-action " *ngFor="let item of testingarray" cdkDrag>{{item}}</div>
      </div>
      </div>

      </div>

      <button type="submit" class="btn btn-primary my-2" translate>saveButtonLabel
        <fa-icon *ngIf="saveIcon" [icon]="saveIcon" [spin]="saveIcon.iconName === 'spinner'"></fa-icon>
      </button>
    </form>

Upvotes: 8

Views: 8054

Answers (2)

obaylis
obaylis

Reputation: 3034

The Angular Material drag / drop API has a moveItemInArray function as seen here

This only supports regular arrays but as per pierNik's answer here on StackOverflow, you can replicate the functionality for a FormArray with the following function:

import { FormArray } from '@angular/forms';

/**
 * Moves an item in a FormArray to another position.
 * @param formArray FormArray instance in which to move the item.
 * @param fromIndex Starting index of the item.
 * @param toIndex Index to which he item should be moved.
 */

export function moveItemInFormArray(
  formArray: FormArray,
  fromIndex: number,
  toIndex: number
): void {
  const dir = toIndex > fromIndex ? 1 : -1;

  const item = formArray.at(fromIndex);
  for (let i = fromIndex; i * dir < toIndex * dir; i = i + dir) {
    const current = formArray.at(i + dir);
    formArray.setControl(i, current);
  }
  formArray.setControl(toIndex, item);
}

Then in your calling code you pass the formArray in place of a normal array.

get formControls(): FormArray {
    return this.form.get('arrayName') as FormArray;
}

constructor() {
    this.form = this.formBuilder.group({
      arrayName: this.formBuilder.array([]),
    });
}

drop(event: CdkDragDrop<string[]>) {
    moveItemInFormArray(
      this.formControls,
      event.previousIndex,
      event.currentIndex
    );
  }

See working Stackblitz here

Upvotes: 22

Ali Turab Abbasi
Ali Turab Abbasi

Reputation: 1133

Currently we cannot use a formControl with Angular Drag and Drop as it works with div and we cannot add formContol to it. Therefore we need to use the event cdkDropListDropped to update our model manually every time an item has been dropped.

Upvotes: 2

Related Questions