ng2-dnd
ng2-dnd copied to clipboard
Multi-list sortable with nested lists
I have a situation similar to the multi-list sortable in the demo except that the target list contains nested lists and items may be dragged from the available boxers panel into any of the nested lists in the First Team panel, including the First Team panel itself. I'm not sure how to achieve that. So far everything just gets dropped into the my equivalent of the First Team panel instead of the nested panel.
I was hoping for behavior similar to this
Hi Jimit,
Did you try to use dropZones property available across all components? With the help of it, you can drag and drop only in allowed places.
Please have a look at examples coming along with a project and let me know does it suited for you?
Yes I am using the dropzones property. I'm basically I'm trying to build a WYSIWYG editor like in the example I linked above. That library is built for NG1 however. Currently nested lists don't work very well because it always drops in the parent.
I will have a look on the weekend.
Hi Jimit,
Several days ago @adrienverge sent PR with new feature uses a custom function to determine where dropping is allowed. Sounds that is what you are looked for. Check example on README.
Hi akserg,
It's not just about limiting where items can be dropped. There are other problems that prevent this scenario from being possible. One is the fact that event handlers don't call event.stopPropagation to prevent parent elements from reacting to drag events on child elements. among others. The implementation of the library i linked above will highlight other differences.
Hi Jimit.
I get it. Let me think about changes I need to incorporate into the library.
On 8 June 2016 at 12:47, Jimit Ndiaye [email protected] wrote:
Hi akserg,
It's not just about limiting where items can be dropped. There are other problems that prevent this scenario from being possible. One is the fact that event handlers don't call event.stopPropagation to prevent parent elements from reacting to drag events on child elements. among others. The implementation of the library i linked above will highlight other differences.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/akserg/ng2-dnd/issues/20#issuecomment-224554182, or mute the thread https://github.com/notifications/unsubscribe/ABVeWbF_heHnCsV48sHLJR6uUgq9zf3Fks5qJp3BgaJpZM4ImkYT .
@akserg Have you come to any conclusions?
HI Jimmit,
Sorry man, not yet. I remember about you concern and do my best to have a look soon.
I have a similar problem, I want to be able to drag&drop panels but I also have lists inside the panels I want to d&d and I have input in the lists that I want to be able to drag to mark everything without moving the panels.
Did ng2-dragula solve your problem without any workarounds?
ng2-angular does support nested list. However, if you do multiple nested list with copy, will definitely has a hard time.
I ended up implementing my own NG2 dnd directives inspired by angular 1's "angular-drag-and-drop-lists" linked in the OP above that work great for handling nested lists, supporting both copy and move with custom placeholders.
@jimitndiaye That's awesome. Do you mind to share it?
For anyone interested: here are the directives I wrote. Here you go: https://gist.github.com/jimitndiaye/63909447f3c08b2e5e9bfd6e1c675545
Still contains some console.debug messages that you may or may not want to remove. Some of the code could use some cleanup and refactoring but it works for me.
@jimitndiaye any help on how to use it? can't manage to get it working.
@jimitndiaye please make a simple tutorial on how to use your directives. Thx.
@redondogabriel @victorblq I wrote those directives some time ago so bear with me. Here's a snippet of production code using the drag/drop directives:
<ul tkDropTarget [allowedTypes]="container.allowedTypes"
[allowExternalSources]="false"
[horizontal]="container.horizontal"
[dragOverClass]="'dndDragOver'"
(allowDrop)="validateChildComponent($event, container.id)"
(onDrop)="addComponent($event, container.id)"
[ngClass]="{horizontal: container.horizontal}"
class="container">
<li *ngFor="let component of container.components"
class="container-item"
[tkDraggable]="component"
[draggedItemType]="component.type"
[dragEffect]="'move'"
[dragClass]="'dndDragging'"
[dragSourceClass]="'dndDraggingSource'">
<ion-toolbar>
<ion-title>{{getComponentName(component.type)}}</ion-title>
<ion-buttons right>
<button primary
title="Settings "
(click)="configureComponent(component)">
<ion-icon name="options"></ion-icon>
</button>
<button danger
title="Delete "
(click)="removeComponent(component.id)">
<ion-icon name="close-circle"></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
<template [tkComponentOutlet]="component"
(componentChanged)="logComponentChanged($event)"></template>
</li>
<li class="dndPlaceholder container-item "
text-center
text-capitalize
*tkDropTargetPlaceholder>Drop here</li>
</ul>
This was written using Angular 2.1.0. I'm not quite up to date any changes in syntax in more recent versions of Angular, if any. It is for a WYSIWYG designer allowing drag and drop of nested components. We needed to support arbitrarily deep nesting while allowing drag and drop to and from containers at any level. The key things to note are:
- The
tkDropTarget
directive is used on the enclosingul
to mark it as a drop zone. - The
horizontal
property on that directive is a boolean used to indicate whether the children are arranged horizontally or vertically - this is used to calculate placeholder placement. In this instance it is bound to a property. If not specified, horizontal is assumed to be false, i.e items are arranged vertically. - The
allowedTypes
property is used to filter which items are allowed to be dropped to in this zone. - The
dragOverClass
is an optional property that allows you to toggle a CSS class when an item is being dragged over the drop zone e.g toggle a border color etc. - I use the
allowDrop
andonDrop
events validate drop items and process dropped items respectively. - Within the enclosing
ul
I use anngFor
to list a bunch of draggableli
using thetkDraggable
directive. These represent the direct children of the container. Marking them as draggable allows you to use drag and drop to reposition items within the container or even move them from one container to another. -
dragClass
is used to toggle a CSS class on the dragged item -
dragSourceClass
is used to toggle a CSS class on the dragged item's original position - The each li contains a template (
tkComponentOutlet
is very similar tongComponentOutlet
) for embedding a component which may itself be a container, thus resulting in nested lists. In my case, any nested containers reuse this same template above resulting in a form of recursion. - Finally the
ul
has anli
marked withtkDropTargetPlaceholder
which basically defines the template for the placeholder element shown during drag and drop
If you have any further questions on individual directive properties there are comments in the original gist for every input property.
The relevant CSS classes for the snippet above are as follows:
/***************************** Dropzone Styling *****************************/
/**
* The drop target should always have a min-height,
* otherwise you can't drop to it when it's empty
*/
ul.container[tkDropTarget] {
min-height: 42px;
width: 100%;
height: 100%;
margin: 0px;
padding-left: 0px;
// background-color: grey;
// margin-top: 10px;
// margin-bottom: 10px;
}
ul.container[tkDropTarget] li{
// background-color: #fff;
border: 1px solid #ddd;
display: block;
padding: 0px;
}
/**
* Reduce opacity of elements during the drag operation. This allows the user
* to see where he is dropping his element, even if the element is huge. The
* .dndDragging class is automatically set during the drag operation.
*/
ul.container[tkDropTarget] .dndDragging {
opacity: 0.7;
}
/**
* Put a border around the drop zone to highlight it during the drag operation
*/
ul.container.dndDragOver {
border: 1px solid blue;
}
/**
* The dndDraggingSource class will be applied to the source element of a drag
* operation. It makes sense to hide it to give the user the feeling that he's
* actually moving it. Note that the source element has also .dndDragging class.
*/
ul.container[tkDropTarget] .dndDraggingSource {
display: none;
}
/**
* An element with .dndPlaceholder class will be added as child of the drop target
* while the user is dragging over it.
*/
ul.container[tkDropTarget] .dndPlaceholder {
background-color: #ddd;
min-height: 42px;
display: block;
position: relative;
}
.container .container-item {
margin: 10px;
}
any news on this?
@lexyfeito we decide to use a different approach creating a popup with the register that we need to drag and drop (Order in our case), one for each level of nesting. Its a workaround of course.
@victorblq yeah i had thought about this but damn it, however what i am doing for now is have to set [dragEnabled] on the parent, if [dragEnabled]="false" it allows me to order the child
@jimitndiaye Are you still using your directives? I'm trying to do the exact same thing and we're moving from the same NG1 library that you were using, and we keep running into dead ends.
Thanks for any help you can give!
Works as expected for list in list. You can have the parent list element rearranged and only the children rearranged between themselves.
Adding [dragEnabled] and [dropZones] works as expected for that case.
Only buggy thing I found is when using handler. No way that I found around this.
If this helps this component have solved all my issues with nested drag and drops and drag handles:
https://github.com/swimlane/ngx-dnd
Follow carefully the README and it works like a charm
Works for me =)
html
<ul dnd-sortable-container [sortableData]="list" [dropZones]="['parent-zone']">
<li *ngFor="let item of list; let index = index" dnd-sortable [sortableIndex]="index" [dragEnabled]="activeParent" (mouseenter)="onSorting(true)">
{{ item.name }}
<ul dnd-sortable-container [sortableData]="item.subList" [dropZones]="['child-zone']">
<li *ngFor="let subItem of item.subList; let subIndex = index" dnd-sortable [sortableIndex]="subIndex" [dragEnabled]="!activeParent" (mouseleave) ="onSorting(true)" (mouseenter)="$event.stopPropagation(); onSorting(false)">{{ subItem.name }}</li>
</ul>
</li>
</ul>
<p>{{ list | json }}</p>
ts
activeParent:boolean = false;
list = [
{ name: '1', subList: [
{ name: '1.1' },
{ name: '1.2' },
{ name: '1.3' }
]
},
{ name: '2', subList: [
{ name: '2.1' },
{ name: '2.2' },
{ name: '2.3' }
]
},
{ name: '3', subList: [
{ name: '3.1' },
{ name: '3.2' },
{ name: '3.3' }
]
}
]
onSorting(isParent:boolean) {
console.log(isParent);
this.activeParent = isParent;
}