Search code examples
angularangular-reactive-formsangular-cdk

Angular CDK drag drop with (reactive) forms


I have this example : https://stackblitz.com/edit/angular-asevei?file=app%2Fcdk-drag-drop-sorting-example.html

everything works, but while dragging the selectbox 'resets' to the first value in the list.

is there any way to fix this? It's only visual, but quite jarring for the user. I have tried using the cdkDragPreview option, but couldn't get it to work.

Component:

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

@Component({
    selector: 'cdk-drag-drop-sorting-example',
    templateUrl: 'cdk-drag-drop-sorting-example.html',
    styleUrls: ['cdk-drag-drop-sorting-example.css'],
})
export class CdkDragDropSortingExample {

    myForm: FormGroup;

    constructor(private fb: FormBuilder) {
    this.myForm = this.fb.group({
        title: ['title'],
        items: fb.array([
        fb.group({
            name: fb.control('1'),
            note: fb.control('quux')
        }),
        fb.group({
            name: fb.control('2'),
            note: fb.control('bar')
        }),
        fb.group({
            name: fb.control('3'),
            note: fb.control('baz')
        })

        ])
    })
    }

    drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.myForm.get('items').controls, event.previousIndex, event.currentIndex);
    moveItemInArray(this.myForm.get('items').value, event.previousIndex, event.currentIndex);
    }
}

Template:

<form [formGroup]="myForm">
    <input formControlName="title" />
    <div cdkDropList id="foo" class="example-list" (cdkDropListDropped)="drop($event)">
        <div class="example-box" *ngFor="let item of myForm.get('items').controls" cdkDrag>
            <span cdkDragHandle>drag</span>
            <div [formGroup]="item">
                <input type="text" formControlName="name">
                <select formControlName="note">
                    <option>foo</option>
                    <option>bar</option>
                    <option>baz</option>
                    <option>quux</option>
                </select>
            </div>
        </div>
    </div>
    {{ myForm.value | json }}
</form>

Solution

  • I have revised @Amir Fawzy's answer by adding a variable in getting the current active note which shows upon dragging the selected box.

    TS:

    activeNote: string;
    enter(i) {
      this.activeNote = this.myForm.get('items')['controls'][i].get('note').value;
    }
    

    HTML:

    <div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
      <div [id]="i" class="example-box" *ngFor="let item of myForm.get('items').controls; let i=index;" cdkDrag #elem="cdkDrag" (mouseenter)="enter(i)">
        <span cdkDragHandle>drag</span>
        <div [formGroup]="item">
          <input type="text" formControlName="name">
          <select formControlName="note">
            <option [hidden]="elem.moved">{{activeNote}}</option>
            <option>foo</option>
            <option>bar</option>
            <option>baz</option>
            <option>quux</option>
          </select>
        </div>
      </div>
    </div>
    

    Here's a duplicate on Stackblitz..