akserg / ng2-dnd

Angular 2 Drag-and-Drop without dependencies
MIT License
839 stars 252 forks source link

Drop on previously dropped items does not trigger drop #174

Open phil123456 opened 7 years ago

phil123456 commented 7 years ago

bug

As I drop an element on the main dnd-droppable, it works, it also works if I drop at the end of the list of previously dropped elements...but if I drop that element on a previously dropped element it does not trigger anything, the dragged element just goes back to where it was in the src list

I am pretty sure I am missing something, but I parsed the tutorials and I cannot find what is wrong in my code

here is the template I use :

<div class="segmentCategoriesAssignationsDiv">
                    <div class="segmentAvailableCategoriesDiv">

                        <div class="panel-primary panel-warning" dnd-droppable [dropZones]="['categories-zone']" (onDropSuccess)="unassignCategory($event)">

                            <div class="panel-heading">{{ getTrad("AvailableCategories") }}</div>
                            <div class="panel-body">
                                <div *ngFor="let category of availableCategories" class="panel panel-default" dnd-draggable [dropZones]="['categories-zone']" [dragData]="category" >
                                    <div class="panel-body">{{category.caption_fr}}</div>
                                </div>
                            </div>

                        </div>
                    </div>
                    <div class="segmentAssignedCategoriesDiv">

                        <div class="panel-primary panel-success" dnd-droppable [dropZones]="['categories-zone']" (onDropSuccess)="assignCategory($event)">

                            <div class="panel-heading">{{ getTrad("AssignedCategories") }}</div>
                            <div class="panel-body" >
                                <div *ngFor="let category of assignedCategories" class="panel panel-default" dnd-draggable [dropZones]="['categories-zone']" [dragData]="category" >
                                    <div class="panel-body">{{category.caption_fr}}</div>
                                </div>
                            </div>

                        </div>
                    </div>
                </div>

here is the relevant part in the controller :

    availableCategories: Object[] = ['Sugar Ray Robinson', 'Muhammad Ali', 'George Foreman', 'Joe Frazier', 'Jake LaMotta', 'Joe Louis', 'Jack Dempsey', 'Rocky Marciano', 'Mike Tyson', 'Oscar De La Hoya'];
    assignedCategories: Object[] = [];

 assignCategory(event: any) {
        console.dir(event.dragData);
        this.availableCategories=this.availableCategories.filter(obj => obj !== event.dragData);
        this.assignedCategories.push(event.dragData);
        this.assignedCategories.sort((a: any, b: any) => {
            return a.caption_fr.localeCompare(b.caption_fr);
        });
    }

    unassignCategory(event: any) {
        console.dir(event.dragData);
        this.assignedCategories=this.assignedCategories.filter(obj => obj !== event.dragData);
        this.availableCategories.push(event.dragData);
        this.availableCategories.sort((a: any, b: any) => {
            return a.caption_fr.localeCompare(b.caption_fr);
        });
    }

I expect any element to be drag/dropped no matter where I drop it (which works in the shop example)

image preview : https://ibb.co/j46pPk

thanks

Iwerzhon commented 7 years ago

Hello, I faced the same bug and found a workaround (that has some limits, and especially in my case it won't work if you're using a stock panel like the Shopping basket example ; I'll explain them at the end of my message) :

In your case @phil123456 maybe the sortable list may be enough as a workaround but it definitely does not solve the problem. If you consider your lists as sortable, you will be able to drop them even in the section where some categories are already placed. You can find an example in the official example here Multi list sortable

However, it has some limits :

Here is an example with a slot and two inputs, where you can sort and drag any of them :

<div class="row">
    <div class="col-sm-3">
        <div class="panel panel-info">
            <div class="panel-heading">STOCK</div>
            <div class="panel-body" dnd-droppable (onDropSuccess)="moveAct($event, 0)">
                <ul class="list-group" dnd-sortable-container [sortableData]="stock">
                    <li *ngFor="let item of stock; let i = index" class="list-group-item"
                        dnd-sortable [sortableIndex]="i" dnd-draggable [dragData]="item">
                        {{item.value}} - {{item.available}}
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <div class="col-sm-3">
        <div class="panel panel-info">
            <div class="panel-heading">Panel 1</div>
            <div class="panel-body" dnd-droppable (onDropSuccess)="moveAct($event, 0)">
                <ul class="list-group" dnd-sortable-container [sortableData]="list[0]">
                    <li *ngFor="let item of list[0]; let i = index" class="list-group-item"
                        dnd-sortable [sortableIndex]="i" dnd-draggable [dragData]="item">
                        {{item.value}}
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <div class="col-sm-3">
        <div class="panel panel-info">
            <div class="panel-heading">Panel 2</div>
            <div class="panel-body" dnd-droppable (onDropSuccess)="moveAct($event, 1)">
                <ul class="list-group" dnd-sortable-container [sortableData]="list[1]">
                    <li *ngFor="let item of list[1]; let i = index" class="list-group-item"
                        dnd-sortable [sortableIndex]="i" dnd-draggable [dragData]="item">
                        {{item.value}}
                    </li>
                </ul>
            </div>
        </div>
    </div>
</div>

And the corresponding component :

stock: Array<any> = [
    {"value": "burger king", "slot": "0", "available": 3},
    {"value": "kfc", "slot": "0", "available": 3},
    {"value": "mcdo", "slot": "1", "available": 3},
    {"value": "quick", "slot": "1", "available": 3}
];
list: Array<any> = [[{"value": "burger king", "slot": "0"}, {"value": "kfc", "slot": "0"}],
[{"value": "mcdo", "slot": "1"}, {"value": "quick", "slot": "1"}]];

moveAct(e, target) {
    this.supprAct(e.dragData);
    this.addAct(e.dragData, target);

}

supprAct(act) {
    for(let i in this.list[act.slot]) {
        if (this.list[act.slot][i].value == act.value) {
            this.list[act.slot].splice(i, 1);
        }
    }
}

addAct(act, target) {
    act.slot = target;
    this.list[target].push(act);
}

Note : I don't decrease the available value in the object here, I only wanted to show that with that method the cell will be removed from Source and added in the Target (it is a correct behaviour), I didn't find a way to keep the source untouched (or at least, modified the way I want). In my code, I store source position in object and target position as parameter of the function (when I have n draggable zones and I want to know what action I must do).

image example An element of the stock is dragged inside the Panel1 and it works regardless of whether you placed it on an already placed element, or directly in the panel itself. However, you won't be able to keep the source element intact (it will be sliced from the source array and added in the target array). As I already said, this behavior seems normal and I only pointed this as a workaround.