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.
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
);
}
}