Search code examples
angulardrag-and-dropformarray

How do I use drag and drop in FormArray with ngx-dnd?


I want to use FormArray in Angular 5.

I have created a list of forms with FormArray and I want to reorganize them with ngx-dnd.

Here is my code:

<div class="ngx-dnd-container" ngxDroppable [model]="formData.controls">
     <div class="ngx-dnd-item" ngxDraggable [model]="line" formArrayName="lines" *ngFor="let line of formData.controls; let i = index;">
         <div [formGroupName]="i"> 
            <textarea formControlName="linedescription"></textarea>
        </div>
    </div>
</div>

This code works fine if the user types into textarea after they drag and drop.

This code doesn't work if the user just drags and drops, in this case my form Array "lines" is the same as before the drag and drop changes the order.

How should I use ng-dnd with FormArray, and can I use a callback method for ng-dnd in my Component to execute something after "drop"?


Solution

  • Ok I have the solution!

    1 - ngx-dnd events

    ngx-dnd library allows us to use many events: drop(), drag(), over(), out(), remove(), cancel(). (Source)

    So, you can call your own method in your component if an item is "dropped". For example:

    form.component.html

    <div (drop)="dropped($event)" 
         class="ngx-dnd-container" 
         ngxDroppable 
         [model]="formData.controls">
    //...
    

    form.component.ts

    dropped(event) {
        alert('Ok drop is Done !')
    }
    

    2 - Update FormArray

    The problem was here: our "drop action" didn't re-order the FormArray.

    Angular's FormArray class exposes many methods to solve this problem. I chose the reset() method because we can pass a value as a parameter and update the entire FormArray.

    reset(value?: any, options?: Object)
    

    Finally, update your method in your form.component.ts

    dropped(event) {
        let linesCopy = [];
        this.lines = this.form.get('lines') as FormArray;
        for(let line of this.lines.controls) {
            linesCopy.push(line.value);
        }
        this.lines.reset(linesCopy);
    }
    

    It works correctly now, you can see a complete working example and source code in this Stackblitz.

    PS: Be careful to use only form class methods when you manipulate forms in Angular. Don't use JavaScript methods to push, remove, set value, etc. Use only FormArray class methods.