1

I am trying to use a FormArray in combination with a cdkDropList. But as soon as I add the corresponding FormGroup, the cdkDrag within that group stops working. So do any other events, like click events of buttons etc, that I removed here for simplicity.

So this works:

<div cdkDropList cdkDropListOrientation="horizontal" style="display: flex;">
  <ng-container *ngFor="let group of formArray.value; let i = index" formArrayName="myFormArray">
    <div cdkDrag>
      <div cdkDragHandle style="padding: 1em; margin: 1em;background: red;">{{i}}</div>
    </div>
  </ng-container>
</div>

This doesnt:

<div cdkDropList cdkDropListOrientation="horizontal" style="display: flex;">
  <ng-container *ngFor="let group of formArray.value; let i = index" formArrayName="compositeContent">
    <div [formGroupName]="i" cdkDrag>
      <div cdkDragHandle style="padding: 1em; margin: 1em;background: red;">{{i}}</div>
    </div>
  </ng-container>
</div>

neither does this:

<div cdkDropList cdkDropListOrientation="horizontal" style="display: flex;">
  <ng-container *ngFor="let group of formArray.value; let i = index" formArrayName="compositeContent">
    <div cdkDrag>
      <div cdkDragHandle style="padding: 1em; margin: 1em;background: red;">{{i}}</div>
      <div [formGroupName]="i"></div>
    </div>
  </ng-container>
</div>

This is the formArray getter:

get formArray(): FormArray {
  return this.form.controls["formArray"] as FormArray;
}

And when adding to the FormArray from existing data from my API, this is how i create a new formArray element:

addElement(data): void {
   this.formArray.insert(this.formArray.length, this.getNewElementFormGroup(data));
}

getNewElementFormGroup(data): FormGroup {
  return new FormGroup({...});
}

Here is a stackblitz. Add [formGroupName]="i" to the cdkDrag div to reproduce the error.

1
  • 1
    NOT iterate over formArray.value, else over formArray.controls, else each change in the controls repaint all the formArray. Well, if you use trackBy you can avoit this happens, but it's unnecesary Commented Apr 25, 2024 at 11:19

1 Answer 1

1

The formArrayName must be the top element above the *ngFor.

The main problem is that, you need to run the for loop on the controls rather the values, this solved the issue.

Working example below!

<form [formGroup]="form">
  <div
    cdkDropList
    cdkDropListOrientation="horizontal"
    style="display: flex;"
    (cdkDropListDropped)="drop($event)"
    formArrayName="myFormArray"
  >
    <ng-container
      *ngFor="
        let formGroup of formArray.controls;
        trackBy: trackBy;
        let i = index
      "
    >
      <div cdkDrag [formGroupName]="i">
        <div cdkDragHandle style="padding: 1em; margin: 1em;background: red;">
          {{ formGroup?.controls?.control?.value }}
        </div>
      </div>
    </ng-container>
  </div>
</form>

ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  form: FormGroup;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.form = new FormGroup({
      myFormArray: new FormArray([]),
    });
    this.addElement(1);
    this.addElement(2);
    this.addElement(3);
  }
  get formArray(): FormArray {
    return this.form.controls['myFormArray'] as FormArray;
  }
  addElement(data): void {
    this.formArray.insert(
      this.formArray.length,
      this.getNewElementFormGroup(data)
    );
  }

  trackBy(i, item) {
    return item;
  }

  getNewElementFormGroup(data): FormGroup {
    return new FormGroup({
      control: new FormControl(data, []),
    });
  }

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(
      this.formArray.controls,
      event.previousIndex,
      event.currentIndex
    );
  }
}

Stackblitz Demo

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

4 Comments

The formArrayName must be the top element above the *ngFor. that had no impact, iterating over the controls rather than the values solved the issue though
@Lapskaus the form array need to be defined only once, since there is only one form array but multiple form groups! if you place on the ngFor, it will get defined multiple times
Right, sorry I missed that, because I have another slightly more complicated structure in the original file, where the formArrayName is on another parent element.
@Lapskaus Then its fine I guess, if it works then fine! since nested forms need to written differently when inside components!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.