components icon indicating copy to clipboard operation
components copied to clipboard

Drag Drop Sortable, mixed orientation support

Open abdulkareemnalband opened this issue 5 years ago β€’ 229 comments

Feature request,

Mixed direction support, such as items placed by flex-wrap: wrap

What is the expected behavior?

Should be able to use sortable drag drop on both vertical and horizontal axis at the same time

What is the current behavior?

Only vertical or horizontal is supported

What are the steps to reproduce?

https://stackblitz.com/github/vbwioxyq

Github repo link

https://github.com/abdulkareemnalband/ng-drag-drop/tree/prebuilt-css

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / β–³ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 7.0.0-beta.4
Node: 8.11.4
OS: win32 x64
Angular: 7.0.0-rc.0
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, platform-server, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.9.0-beta.4
@angular-devkit/build-angular     0.9.0-beta.4
@angular-devkit/build-optimizer   0.9.0-beta.4
@angular-devkit/build-webpack     0.9.0-beta.4
@angular-devkit/core              7.0.0-beta.4
@angular-devkit/schematics        7.0.0-beta.4
@angular/cdk                      7.0.0-beta.2
@angular/cli                      7.0.0-beta.4
@angular/flex-layout              6.0.0-beta.16
@angular/material                 7.0.0-beta.2
@ngtools/webpack                  7.0.0-beta.4
@schematics/angular               7.0.0-beta.4
@schematics/update                0.9.0-beta.4
rxjs                              6.3.3
typescript                        3.1.1
webpack                           4.19.1

abdulkareemnalband avatar Oct 01 '18 11:10 abdulkareemnalband

@jelbourn hey ,have any progress about this issue now?

100cm avatar Oct 22 '18 07:10 100cm

Are there any workarounds for drag and drop in mat-grid-list (reordering items in the grid)?

I have an idea, to mark each item in the grid as 'cdkDropList', and handle reordering as dropping item from one cdkDropList to another

  <mat-grid-list [cols]="photoCols"
                       rowHeight="1:1"
                       gutterSize="20px">
          <mat-grid-tile *ngFor="let photo of photos$ | async"
                         cdkDrag
                         cdkDropList
                         (cdkDragEnded)="dragEnded($event)">
            <app-photo-item [Photo]="photo"
                            (OnSelectionChange)="onSelectionChange($event)"></app-photo-item>
          </mat-grid-tile>
</mat-grid-list>

If it will work, I will provide demo

IhorHolovatsky avatar Oct 30 '18 22:10 IhorHolovatsky

How come the demo gif shows different with what it can provide ? https://cdn-images-1.medium.com/max/800/1*i30ZQdBC7CKbXXdOrUNQcg.gif

vupham-zingbox avatar Oct 30 '18 23:10 vupham-zingbox

@vupham-zingbox +1 , i want to know how to implement what the demo shows

100cm avatar Oct 31 '18 01:10 100cm

@IhorHolovatsky Nice idea but only works when no of items in per row is known before hand But when content reflows on window resize, this is not pretty

abdulkareemnalband avatar Oct 31 '18 05:10 abdulkareemnalband

Would be nice if what's shown in the demos was actually backed up by the framework code.

demisx avatar Nov 01 '18 19:11 demisx

Copy and pasting my comment from #13889 since it was a duplicate:

To give an example of the issue, I have a horizontal list of photos that needs to wrap (using fxLayout="row wrap", as the issue description states). However, when it spans multiple lines, it breaks:

drag

BovineEnthusiast avatar Nov 02 '18 18:11 BovineEnthusiast

Any progress about this issue?

ducalai avatar Nov 12 '18 09:11 ducalai

Almost a month has passed , any update now ?

100cm avatar Nov 12 '18 09:11 100cm

Any progress about this issue?

eduardogiorgio avatar Nov 12 '18 23:11 eduardogiorgio

@MrSnappingTurtle Do you have demo code of multiple list wrap? Thanks :)

PhoebePan avatar Nov 14 '18 00:11 PhoebePan

Although i know to achieve this feature is very hard. but I still want to ask "any progress about this issue?"

100cm avatar Nov 21 '18 04:11 100cm

Is there any progress on this one? Or eventually any idea of when it would be implemented?

hristo-iliev avatar Nov 22 '18 08:11 hristo-iliev

This is difficult to implement in the current way it was implemented in the CdkDrag directive. The positioning of the placeholder, and the positions of each item that is clearing way for the placeholder, are done by setting inline CSS transforms to each of the involved elements. This is done in a predictable fashion, meaning that since the dimensions of the dragged element are known from the moment it is dragged, it can be calculated for each of the elements in the list how many pixels they should be shifting back/forth. The calculation is eased further by limiting the transforms to a single axis, allowing the transforms to ignore the widths (or heights in horizontal mode) of elements in the list.

On the other hand, when a list is wrapping, the wrapped elements are being wrapped in an arbitrary manner, calculated by the browser. When so, it is impossible to translate (transform) list elements in a similar predictability, because it is not known (upon drag event) which list elements are wrapping, and where they are positioned relatively within the list.

To circumvent that, upon every drag event, a new snapshot of the list (and all connected lists) must be generated, where the list DOM is being traversed, and each list element gets registered for width and height (practically creating a matrix). Considering that Material devs tried to solve this directive through a performative approach, traversing the DOM and registering each element is not an option as it taxes the browser significantly, and veers from the predictability approach.

Alternatively, a middle ground can be implemented. It is less performative than current solution, but at the same time does not require any heavy calculations. Instead of shifting elements via transform, the placeholder element should be moved (currently it remains in the place of the original dragging element), to be positioned before or after the target drop element. It then could animate the max-height property (or max-width for horizontal). This, as I mentioned, is less performative, but it does allow to shift the placeholder anywhere in the list - even in a wrapping list.

AsafAgranat avatar Nov 28 '18 11:11 AsafAgranat

for time being you can take a look this - https://github.com/smnbbrv/angular-sortablejs-demo/tree/master/src/app/examples/multiple-lists

allenz0810 avatar Dec 07 '18 21:12 allenz0810

Thank you! I appreciate your help, I'll check it out.

Best regards, Flo

flolu avatar Dec 13 '18 20:12 flolu

here is a workaround with using multiple lists https://stackblitz.com/edit/angular-nuiviw

c3rber avatar Dec 13 '18 21:12 c3rber

@c3rber That was wonderful! You are my best friend! Thanks a lot ! i will try it.

100cm avatar Dec 14 '18 02:12 100cm

@c3rber Wow, it's work for me, thx.

But.... Any OFFICIAL progress about this issue? It's for make and standar to this.

Thx

NurGuz avatar Dec 17 '18 08:12 NurGuz

@c3rber Yeah, it is working fine.

Is there a way of animating @c3rber workaround?

RobinBomkampDv avatar Dec 18 '18 08:12 RobinBomkampDv

@c3rber

Don't work correcty.

If u add a button, for example, for add new items and after reorder the items doesn't work. If you move the ultimate element in tother place don't reorganize correctly

I think that the problem is <div class="example-container" cdkDropListGroup> besacuse @ViewChild(CdkDropListGroup) listGroup: CdkDropListGroup<CdkDropList>; don't update correctly

NurGuz avatar Dec 18 '18 15:12 NurGuz

@NurGuz Check it out. I added an example. https://stackblitz.com/edit/angular-nuiviw

Placeholder was moved outside the model and also type of data changed from string[][] to items: Array<number> = [1, 2, 3, 4, 5, 6, 7, 8, 9];

c3rber avatar Dec 19 '18 23:12 c3rber

@c3rber GJ man! It's okey!

I'm going to try it in my application

NurGuz avatar Dec 20 '18 09:12 NurGuz

I really appreciate your help about this issue, but someting isn't quite working:

In the Stackblitz demo (https://stackblitz.com/edit/angular-nuiviw) everything works fine. I exactly coppied the code into my project and the following happens:

[image: dragdropbug.png] Sometimes, the wrong item is greyed out. But when releasing the mouse the dragged item goes to the right place.

It would be really nice if someone can help me out with this!

Thanks, Flo

On Thu, 20 Dec 2018 at 10:22, NurGuz [email protected] wrote:

@c3rber https://github.com/c3rber GJ man! It's okey!

I'm going to try it in my application

β€” You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/angular/material2/issues/13372#issuecomment-448929235, or mute the thread https://github.com/notifications/unsubscribe-auth/Agm6TYSlED1-n9huliaOWscdghRVR9bhks5u61bLgaJpZM4XB1j0 .

flolu avatar Dec 23 '18 17:12 flolu

@c3rber Nice solution, although it doesn't work quite well on divs with different width-sizes. Any solution for this?

GekkePeer avatar Jan 07 '19 09:01 GekkePeer

@crisbeto This issue should labeled "critical" as most use-cases with responsive design need multi-line support.

raphael22 avatar Jan 08 '19 10:01 raphael22

@c3rber Fantastic solution. This is exactly what we were looking for.

daveschwerstein avatar Jan 08 '19 19:01 daveschwerstein

Last year , I subscribe this problem , today i hope this problem could be solve in 2019. waiting for it.

100cm avatar Jan 10 '19 03:01 100cm

Hi all,

If you want that example from @c3rber to work with material grid list (singular), then just change the content of the example container to :

<mat-grid-list
    cdkDropList
    [cdkDropListEnterPredicate]="enter"
    (cdkDropListDropped)="drop()"
    #placeholder
    [gutterSize]="15"
    [cols]="1"
    [rowHeight]="100"></mat-grid-list>
  
  <mat-grid-list cdkDropList *ngFor="let item of items"
       [cdkDropListEnterPredicate]="enter"
       (cdkDropListDropped)="drop()"
       [gutterSize]="15" [cols]="2" [rowHeight]="100">
    <mat-grid-tile
      cdkDrag
      class="example-box"
      [colspan]="1"
      [rowspan]="2">
      {{item}}
    </mat-grid-tile>
  </mat-grid-list>

and make sure to change the width of example box in the css to :

width: 200px !important;

I'll try and get a demo up over the weekend. Feel free to change columns and rows to see what suits.

I'll be looking further into why this actually works because in my mind, there should be two grid lists and a For loop to create tiles, but in this example, there are many grid lists, one for each tile and then the placeholder as well.

image

Note: obviously, you'd have to also include material package and correct module.

CAspeling avatar Jan 11 '19 04:01 CAspeling

@skatestyle Do you have the same version of the cdk installed as in the demo (7.1.1)?

devtician avatar Jan 12 '19 03:01 devtician

@devtician Nope, I am currently using 7.2.0

flolu avatar Jan 12 '19 07:01 flolu

@skatestyle You need to switch to version 7.1.1, because I found that after 7.1.1 some behaviour was changed (not sure what exactly), but this solved it for me.

devtician avatar Jan 12 '19 14:01 devtician

@devtician even in the demo itself without changing or copying anything over to anywhere else, there is the bug on position 0 when dragging from elsewhere (generally when going diagonally). sometimes it happens, sometimes it doesn't. I've not been able to make it happen when dragging to any other position.

CAspeling avatar Jan 13 '19 07:01 CAspeling

@crisbeto @jelbourn Can you give us any kind of update regarding this issue? I don't think that most of the community or if i speak for myself expect that you give us a timeframe for this feature or that this will be implemented at all. You do amazing work here, but some kind of communication would be helpful. There is no comment from the Angular team on this pretty high discussed issue. I just want to ask you kindly to make communication a little bit more transparent so that we can understand why there is no update since this issue was opened.

pas2al avatar Jan 16 '19 17:01 pas2al

@StephenFluin posted on his blog a really good demo: https://stackblitz.com/edit/drag-drop-dashboard Link to post: https://fluin.io/blog/things-I-wish-I-knew-about-CDK-drag-drop

I've been playing around and it works well, you can achieve some cool things... My test: https://stackblitz.com/edit/yag-dnd-dashboard

It even works on IE11 πŸ‘

yasselavila avatar Jan 25 '19 23:01 yasselavila

A few notes after implementing the workaround:

  • the examples have some extra stuff I didn't need -- at its core, all it does is wrap each item in its own container (ckdDropList), storing the index (in the list) as the data the handler entered uses for rearranging the list
  • sometimes the rearranging action doesn't work (see below)
image

See my Gist for relevant code.

Dragging an item initially from the index 0 to the index 4 as follows: 0 -> 1, 1 -> 2, 2 -> 5, 5 -> 4, the last 5 -> 4 drag does not register.

For now actively telling the (injected) change detector (cd: ChangeDetectorRef) to detect changes as soon as a new ckdDropList works at the cost of firing the handler one extra time each move (0 -> 1, 1 -> 1 instead of just 0 -> 1 for example) as the view updates.

krusli avatar Jan 31 '19 05:01 krusli

We need this too.

willbeaufoy avatar Feb 04 '19 14:02 willbeaufoy

@krusli works perfectly, thank you πŸ‘

flauc avatar Feb 10 '19 11:02 flauc

@krusli Thx for your solution! Using it with cards, also with animation. Need only some little fixing to work perfect. One question. You wrote β€ž... some extra stuff I didn't needβ€œ. What do you mean with that? Is there a other behaviour to the workaround of c3rber?

mkarras avatar Feb 10 '19 22:02 mkarras

@mkarras taking a look at it again, it's just the lack of manually removing and adding HTML elements on the page and notifying the ChangeDetectorRef instead. From the other StackBlitz example it's just the lack of the QueryList stuff.

Functionally they should all be the same/have the same basic idea: get around the problem by using our own data for each cdkDropList and cdkDrag (indices in my example for both cdkDropList and cdkDrag, although you can use different things depending on what you need), then handle the rearranging logic ourselves using said data (calling moveItemsInList or other helpers as needed).

krusli avatar Feb 10 '19 23:02 krusli

@krusli hey man, but how it will look for this approach? https://stackoverflow.com/questions/54590576/angular-cdk-drag-and-drop-resize mb u have an idea..

denkerny avatar Feb 11 '19 10:02 denkerny

I'm using dnd2 now. Hope to have the official solution

qichangjun avatar Feb 15 '19 09:02 qichangjun

@qichangjun hm, but it looks like it isn't supported ( https://github.com/akserg/ng2-dnd/issues/274

denkerny avatar Feb 15 '19 12:02 denkerny

@kernyden I've only looked at your code a bit so I'm not quite sure about what you're using cdkDrag for. Are you using it to rearrange items, or just to resize the boxes on your page?

krusli avatar Feb 15 '19 12:02 krusli

@krusli im using cdkDrag for rearrange, grabber and resizable (custom) directives r used for resize.

denkerny avatar Feb 15 '19 16:02 denkerny

@kernyden I probably have dragging problems due to size issues: https://github.com/angular/material2/issues/14093#issuecomment-462395200

In your previous comment you referred to StackOverflow: https://stackoverflow.com/questions/54590576/angular-cdk-drag-and-drop-resize However the source is incomplete. Can you please provide the complete source (or better make a StackBlitz, making communication for fixes easier).

FritzHerbers avatar Feb 15 '19 17:02 FritzHerbers

This would be nice to have it sorted soon. We also need it.

CarlosTorrecillas avatar Feb 20 '19 15:02 CarlosTorrecillas

I have found this incredibly interesting project here: (has no 3rd party dependencies).

https://github.com/swiety85/angular2gridster

DEMO

andreElrico avatar Feb 20 '19 16:02 andreElrico

@yasselavila Your solution worked well for me!

You can save the hassle of using @ViewChildren to get the drop list connections by wrapping the grid list in a cdkDropListGroup:

<div class="wrapper" cdkDropListGroup>
    <mat-grid-list [cols]="responsiveColumns" rowHeight="1:1">
        <mat-grid-tile *ngFor="let item of items; let i = index">
            <cdk-drop-list [cdkDropListData]="i">
                <div cdkDrag (cdkDragDropped)="handleDrop($event)" [cdkDragData]="i">
                    <!-- contents of draggable item -->
                </div>
            </cdk-drop-list>
        </mat-grid-tile>
    </mat-grid-list>
</div>

I have some logic that is called when an item is dropped, so I used cdkDragDropped rather than cdkDragEntered, which would have triggered too many calls.

spiritintheshiftkey avatar Feb 20 '19 16:02 spiritintheshiftkey

@spiritintheshiftkey Thank you. Your suggestion helped me reduce some code πŸ‘

yasselavila avatar Feb 20 '19 21:02 yasselavila

Also with CSS columns... See here https://stackblitz.com/edit/angular-cdk-drag-columns

UPDATE: https://stackblitz.com/edit/angular-cdk-drag-columns-fixed I got it mostly working with using the cdkDropListGroup. However there is still something a little buggy with it. Interestingly its seems you can have cdkDrag and cdkDropList on the same element also. See my example here. I think most of the same principles will apply to flexbox layout.

If you want to see the buggy-ness, try dragging the first element to the third position then back up in one move... it doesn't let you do it, but if you let go then drag it back up it works.

<div class="container" cdkDropListGroup>

<hello class="item" 
    *ngFor="let item of items; let i = index"
    cdkDropList
    [cdkDropListData]="{ item: item, index: i }" (cdkDropListDropped)="drop($event)"
    cdkDrag
    [name]="item">
    <span cdkDragHandle>= </span>
</hello>

<div *cdkDragPlaceholder></div>

</div>
...
  drop(event: CdkDragDrop<{ item: string, index: number}>) {
    moveItemInArray(this.items, event.previousContainer.data.index, event.container.data.index);
  }

jpduckwo avatar Feb 21 '19 22:02 jpduckwo

@spiritintheshiftkey I am finding difficulties in implementing cdkDropListGroup with multiple grid-lists. Can you help me if you have a solution for transferring items between grid lists?

robinprashanth avatar Feb 28 '19 09:02 robinprashanth

@robinprashanth I think the main change from my example above is to handle the dragdata/droplistdata a little differently: you have to make sure that each drag item/drop list pair has a unique number for its data. So if you want to have n grid tiles in each grid list, and generate the grid lists like <mat-grid-list *ngFor="let list of lists; let k = index">, you'd do [cdkDroplistData]="n * k + i" and [cdkDragData]="n * k + i".

I haven't tried keeping each list in a separate array, so I've only tested this with <mat-grid-tile *ngFor="let item of items.slice(n*k, n*(k+1)); let i = index">, but it should be fairly simple to handle separate arrays instead, with some changes to the handleDrop() method.

spiritintheshiftkey avatar Mar 01 '19 14:03 spiritintheshiftkey

@spiritintheshiftkey thanks for the help.Let me try.

robinprashanth avatar Mar 04 '19 10:03 robinprashanth

@crisbeto @devversion @jelbourn Gentleman, is this issue planned to be fixed this year ?

Sincerely, Devs.

raphael22 avatar Mar 08 '19 14:03 raphael22

Nevermind Best alternative: https://github.com/valor-software/ng2-dragula

raphael22 avatar Mar 11 '19 13:03 raphael22

Just a thought, make do with vertical list as it is & set the CSS width to make it smaller enough for it to become multi-columns GRID. Would that work better?

fletchsod-developer avatar Mar 12 '19 19:03 fletchsod-developer

here is a workaround with using multiple lists https://stackblitz.com/edit/angular-nuiviw

I copied and pasted exact code as shared by @c3rber but getting the below error :

Uncaught TypeError: Cannot read property 'children' of null at CdkDropList.AppComponent.enter [as enterPredicate] (app.component.ts:75) at DropListRef.CdkDropList._dropListRef.enterPredicate (drag-drop.es5.js:3395) at DropListRef.push../node_modules/@angular/cdk/esm5/drag-drop.es5.js.DropListRef._canReceive (drag-drop.es5.js:2199) at drag-drop.es5.js:2176 at Array.find () at DropListRef.push../node_modules/@angular/cdk/esm5/drag-drop.es5.js.DropListRef._getSiblingContainerFromPosition (drag-drop.es5.js:2176) at DragRef.push../node_modules/@angular/cdk/esm5/drag-drop.es5.js.DragRef._updateActiveDropContainer (drag-drop.es5.js:930) at SafeSubscriber.DragRef._pointerMove [as _next] (drag-drop.es5.js:298) at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:196) at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:134)

Please help.

cool1nik avatar Mar 15 '19 07:03 cool1nik

We need this too.

gustavshf avatar Mar 18 '19 07:03 gustavshf

Hi, just did the code for me to simply log the mouseEnter. On the drag div, simply added (mouseenter)="enter(index)"

<div fxLayout="row wrap"
     fxLayoutGap="grid"
     fxLayoutAlign="left center"
     cdkDropList
     class="drag-boundary"
     (cdkDropListDropped)="drop($event)">
  <div *ngFor="let item of items; let index = index; trackBy: trackByIndex"
       [style.padding]="'5px'"
       cdkDragBoundary=".drag-boundary"
       cdkDrag
       (mouseenter)="enter(index)">

In the TS:

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(you Array, event.previousIndex, this.currentDropIndex);
  }
  enter(index) {
    this.currentDropIndex = index;
  }

Of course this is for a single array to re-order elements

There is still a lot of visual glitch, but at least it's able to re-order properly. Hope this helps.

Francois-Belanger avatar Mar 18 '19 12:03 Francois-Belanger

Thanks @spiritintheshiftkey! Your soltution did wor well for me (drag and drop across multiple lines)

<div class="wrapper" cdkDropListGroup>
    <mat-grid-list [cols]="responsiveColumns" rowHeight="1:1">
        <mat-grid-tile *ngFor="let item of items; let i = index">
            <cdk-drop-list [cdkDropListData]="i">
                <div cdkDrag (cdkDragDropped)="handleDrop($event)" [cdkDragData]="i">
                    <!-- contents of draggable item -->
                </div>
            </cdk-drop-list>
        </mat-grid-tile>
    </mat-grid-list>
</div>

I have some logic that is called when an item is dropped, so I used cdkDragDropped rather than cdkDragEntered, which would have triggered too many calls.

  public handleDrop(event: CdkDragDrop<any>) {

    const oldPosition = event.item.data;
    const newPosition = event.container.data;

    moveItemInArray(theArray, oldPosition, newPosition);
}

Guy-Incognito avatar Mar 19 '19 10:03 Guy-Incognito

Hi ! Thanks @Francois-Belanger for your solution, it works but visual glitches are annoying Is there any progress about this issue ?

SimonKnu avatar Apr 11 '19 14:04 SimonKnu

Hi ! Thanks @Francois-Belanger for your solution, it works but visual glitches are annoying Is there any progress about this issue ?

Hi, sadly no, I'm waiting for Angular Team to fix this. For my use case it's good enough for now. Hope someone find a better solution.

Francois-Belanger avatar Apr 11 '19 14:04 Francois-Belanger

After losing hope of a fix for this problem, I found this library more suitable and much easier to work with: https://tiberiuzuld.github.io/angular-gridster2/ //----------------- https://ourcodeworld.com/articles/read/663/top-5-best-draggable-droppable-and-resizable-grid-layout-designer-jquery-and-javascript-plugins

hmzahzeade avatar Apr 15 '19 10:04 hmzahzeade

I guess one solution for this can use the drop() method but unfortunately I couldn't make it work. Can someone help what is wrong?

   entered(dragObj: CdkDragEnter) {
        dragObj.container.drop(dragObj.item, dragIndexI, dragElemContainer, false);
    }

so the idea is using GroupList and whenever one item from the first list entered to the other list we manually interchange the places. put the drag item in the second and second in the first. I don't know if this idea works or not but the event trigger before it moves both elements into the same List container, I tried to do it by moving the elements from one container to other but it didn't work properly.

aryaroudi avatar Apr 15 '19 15:04 aryaroudi

@c3rber thank for your code. I use verson 7.3.7 and in my case the enteredPredicate is called for every all lists not only the one which is entered. so your setup will not work, is this a change in 7.3.7 or bug? do you have a work around for this?

tibistibi avatar Apr 17 '19 08:04 tibistibi

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

c3rber avatar Apr 17 '19 08:04 c3rber

@c3rber cool.

can you change this: parentNode parentElement to avoid the compile error (node somehow does not have children defined)

tibistibi avatar Apr 17 '19 09:04 tibistibi

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

You are using CdkDropListContainer which is deprecated and probably in the future will break the code

aryaroudi avatar Apr 17 '19 09:04 aryaroudi

@tibistibi sure, have changed to parentElement @aryaroudi thanks, didn't notice. replaced with CdkDropList

c3rber avatar Apr 17 '19 09:04 c3rber

@c3rber nice! there are still some warnings about private methods used. i will look into that (or if you have a solution that would be fine too :)

tibistibi avatar Apr 17 '19 09:04 tibistibi

@c3rber nice! there are still some warnings about private methods used. i will look into that (or if you have a solution that would be fine too :)

Correct. _getConstrainedPointerPosition or _cacheOwnPosition

aryaroudi avatar Apr 17 '19 09:04 aryaroudi

those are used to know which is active. i think this can also be done with mouse enter and leave like this: (mouseenter)="enter(i)" (mouseleave)="leave(i)

so in full it will be this:

  <div cdkDropList id="candidate_list_{{i}}" *ngFor="let item of items; let i = index" (cdkDropListDropped)="drop($event)"
    [cdkDropListEnterPredicate]="enterPredicate" (mouseenter)="enter(i)" (mouseleave)="leave(i)">

tibistibi avatar Apr 17 '19 09:04 tibistibi

@tibistibi yes, but not sure that this will work well on touch screen devices. I have replaced undocumented private methods with local ones.

c3rber avatar Apr 17 '19 10:04 c3rber

great job @c3rber πŸ‘

aryaroudi avatar Apr 17 '19 13:04 aryaroudi

@c3rber good point, i will investigate and thanks for improving the code!

tibistibi avatar Apr 17 '19 13:04 tibistibi

@c3rber you are right mouseenter did not work on my tablet!

some minor feedback: to make my strict compiler and formatter happy i needed to do 3 things:

  1. change let to const
  2. add {} for all if's
  3. make functions private methods

tibistibi avatar Apr 17 '19 14:04 tibistibi

and some more: 4. add implements AfterViewInit 5. all == and != should be === and !== 6. " should be '

thanks @c3rber i used your code to create a select candidate page: https://stackblitz.com/edit/angular-yysvkf

tibistibi avatar Apr 17 '19 21:04 tibistibi

@c3rber Thanks, it works! But there's a minor error in your code, it will not affect ng serve but ng build --prod will have "Expected 0 arguments, but got 1." issue. Here is the issue In app.component.ts this dropListDropped() method receive no argument dropListDropped() But in app.component.html you supply a '$event' into dropListDropped() <div cdkDropList [cdkDropListEnterPredicate]="dropListEnterPredicate" (cdkDropListDropped)="dropListDropped($event)"></div><div cdkDropList *ngFor="let item of items" [cdkDropListEnterPredicate]="dropListEnterPredicate" (cdkDropListDropped)="dropListDropped($event)">

Just remove $event or add an argument in dropListDropped function.

tldr; change dropListDropped() to dropListDropped(event)

khaukheng avatar Apr 18 '19 05:04 khaukheng

here is a workaround with using multiple lists https://stackblitz.com/edit/angular-nuiviw

I copied and pasted exact code as shared by @c3rber but getting the below error :

Uncaught TypeError: Cannot read property 'children' of null at CdkDropList.AppComponent.enter [as enterPredicate] (app.component.ts:75) at DropListRef.CdkDropList._dropListRef.enterPredicate (drag-drop.es5.js:3395) at DropListRef.push../node_modules/@angular/cdk/esm5/drag-drop.es5.js.DropListRef._canReceive (drag-drop.es5.js:2199) at drag-drop.es5.js:2176 at Array.find () at DropListRef.push../node_modules/@angular/cdk/esm5/drag-drop.es5.js.DropListRef._getSiblingContainerFromPosition (drag-drop.es5.js:2176) at DragRef.push../node_modules/@angular/cdk/esm5/drag-drop.es5.js.DragRef._updateActiveDropContainer (drag-drop.es5.js:930) at SafeSubscriber.DragRef._pointerMove [as _next] (drag-drop.es5.js:298) at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:196) at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:134)

Please help.

The work around to this is using lesser version of Angular and Angular CDK.

Here's what worked for me,

   "@angular/animations": "7.1.2",
    "@angular/cdk": "7.1.1",
    "@angular/common": "7.0.1",
    "@angular/compiler": "7.0.1",
    "@angular/core": "7.0.1",
    "@angular/forms": "7.0.1",
    "@angular/http": "7.0.1",
    "@angular/platform-browser": "7.0.1",
    "@angular/platform-browser-dynamic": "7.0.1",
    "@angular/platform-server": "7.0.1",
    "@angular/router": "7.0.1",
    "@ng-toolkit/serverless": "7.1.1",
    "@nguniversal/express-engine": "7.1.1"

It doesnot work with above versions of Angular CDK

amunir9 avatar Apr 22 '19 07:04 amunir9

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

@cool1nik @amunir9 refer to this latest update that works with @angular/cdk 7.3.7

khaukheng avatar Apr 22 '19 07:04 khaukheng

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

THANK YOU SO MUCH

alamgirqazi avatar Apr 22 '19 07:04 alamgirqazi

Could someone do a npm package for this, so it could easily be integrated ?

Francois-Belanger avatar Apr 22 '19 11:04 Francois-Belanger

I had situation where there is grid items with differing sizes and that caused a lot of not cool behaviour when sorting items on grid. After some fixes to the moveItemInArray indexes it works now in ok level (not perfect at all).

First I went using the solution where cdkDropList directive was used as a child of mat-grid-tile component and sorting is done on cdkDropListEntered event.

<mat-grid-list [cols]="mobileView ? 1 : 2" rowHeight="56px" gutterSize="24px"> <mat-grid-tile *ngFor="let item of grid; trackBy: trackByKey; let i = index;" class="tile" [id]="'grid-item-' + item.key" [colspan]="mobileView ? 1 : item.gridSettings.sizex" [rowspan]="dragOn !== item.key ? item.gridSettings.sizey : (placeholderSizeY || item.gridSettings.sizey)"> <div [class.activedrag]="dragOn === item.key" cdkDropList [cdkDropListConnectedTo]="drops" [cdkDropListData]="i" (cdkDropListEntered)="dropListEnter($event)"></div> </mat-grid-tile> </mat-grid-list>

By using that one starts to get weird problems when dragging items on top of differently sized grid items. So in the dropListEnter function one can correct a bit that behavior:

`dropListEnter($event: CdkDragEnter) { let indexCorrection: number = 0;

if ($event.container.data > $event.item.data) {
  if (this.grid[$event.item.data]['gridSettings']['sizex'] === 2) {
    // This fill fix situation where one is dragging 2 column chart downwards and chart below is 1 column chart
    if ($event.container.data - $event.item.data === 1
    && this.grid[$event.container.data]['gridSettings']['sizex'] === 1
    && this.grid[$event.container.data + 1]
    && this.grid[$event.container.data + 1]['gridSettings']['sizex'] === 1) {
      indexCorrection--;
    }
  } else {
    // This will fix situation where one is dragging item over 2 column chart
    for (let i = $event.item.data, len = $event.container.data; i < len; i++) {
      if (this.grid[i]['gridSettings'] && this.grid[i]['gridSettings']['sizex'] === 2) {
        indexCorrection++;
      }
    }
  }

  // This will fix situation where one is dragging item downwards when dragged item is bigger than item on right
  if (indexCorrection === 0 && $event.container.data - $event.item.data === 3) {
    const dragEl: HTMLElement = document.getElementById(`grid-item-${ this.grid[$event.item.data]['key'] }`);
    const nextEl: HTMLElement = document.getElementById(`grid-item-${ this.grid[$event.item.data + 1]['key'] }`);

    if ( dragEl && nextEl
        && dragEl.offsetLeft < nextEl.offsetLeft
        && (dragEl.offsetTop + dragEl.offsetHeight) > (nextEl.offsetTop + nextEl.offsetHeight)
    ) {
      indexCorrection++;
    }
  }

  // This will fix situation where one is dragging item downwards when dragged item is bigger than next element
  // and items wouldn't be on their natural places because of item on left is bigger than next element
  if (indexCorrection === 0 && $event.container.data - $event.item.data === 2) {
    const prevLeftEl: HTMLElement = document.getElementById(
      `grid-item-${ this.grid[$event.item.data - 1]['key'] }`
    );
    const dragEl: HTMLElement = document.getElementById(`grid-item-${ this.grid[$event.item.data]['key'] }`);
    const nextEl: HTMLElement = document.getElementById(`grid-item-${ this.grid[$event.item.data + 1]['key'] }`);

    if ( prevLeftEl && dragEl && nextEl
        && dragEl.offsetLeft > nextEl.offsetLeft
        && (prevLeftEl.offsetTop + prevLeftEl.offsetHeight) > (dragEl.offsetTop + nextEl.offsetHeight)
    ) {
      indexCorrection++;
    }
  }
}

// This will fix situation where one is dragging item upwards when dragged item is smaller than target element
// and thus mouse cursor would end up on top of wrong element
if (
  $event.container.data < $event.item.data &&
  this.grid[$event.container.data]['gridSettings']['sizey'] > this.grid[$event.item.data]['gridSettings']['sizey']
) {
  const dragEl: HTMLElement = document.getElementById(`grid-item-${ this.grid[$event.item.data]['key'] }`);
  const targetEl: HTMLElement = document.getElementById(`grid-item-${ this.grid[$event.container.data]['key'] }`);

  if ( dragEl.offsetTop > targetEl.offsetTop) {
    this.placeholderSizeY = this.grid[$event.container.data]['gridSettings']['sizey'];
  } else {
    this.placeholderSizeY = null;
  }
} else if ($event.container.data === $event.item.data) {

} else {
  this.placeholderSizeY = null;
}

// I made custom move item in array function but idea is like moveItemInArray(grid, previousIndex, newIndex - indexCorrection)
moveItemInArray(this.grid, this.grid[$event.item.data], this.grid[$event.container.data - indexCorrection]);

this.cdRef.markForCheck();
this.cdRef.detectChanges();

}`

Hopefully this helps if someone is working with 2 column grid where grid items have differing sizes.

I really hope that mat-grid-list and cdk-drag-drop would have some kind of official relationship in the future so that one doesn't have to use forced marriage between them.

masaala avatar Apr 25 '19 10:04 masaala

@crisbeto will this bug be fixed in next release of CDK, planned for Q1 β†’ Q2 2019 (January - June)?

seriojak avatar May 07 '19 07:05 seriojak

@masaala please post a stackblitz. @seriojak its not a bug, its just a very important feature that hasnt been covered yet.

andreElrico avatar May 08 '19 19:05 andreElrico

We definitely plan on doing this at some point, but it's actually quite a difficult feature to implement. We don't currently have an ETA on this. If someone would like to contribute something here, please follow the process outlined in our contributing guide and start a discussion before committing to any code.

jelbourn avatar May 16 '19 17:05 jelbourn

https://stackblitz.com/edit/angular-dyz1eb

This works also with angular material 8. Thanks

tommy86r avatar Jun 04 '19 07:06 tommy86r

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

Cool

opassion avatar Jun 06 '19 18:06 opassion

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

Thanks a lot!

heloufir avatar Jun 10 '19 11:06 heloufir

Is there any official solution to make it with mat-grid-list??

Estevete avatar Jul 03 '19 10:07 Estevete

Can we add a critical label on this issue as it is one of the most requested features?

alator21 avatar Jul 23 '19 18:07 alator21

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

I created a dynamic component based on this stackblitz example. https://github.com/sam-1994/ngx-drag-and-drop-grid

Currently it is only tested with Angular 8.x. Feel free to include it in your projects or giving feedback for improvements

sam-1994 avatar Aug 01 '19 11:08 sam-1994

@c3rber what is the license for your code in https://stackblitz.com/edit/angular-dyz1eb ?

It is so good that I wanted to copy, modify and use this code, but not sure if it is legal. :-)

semykov avatar Aug 15 '19 11:08 semykov

Hi everyone, i would like to share my solution for the mixed orientation problem, here are the steps: 1- just put the cdkDropList as always: <div class="container" cdkDropList (cdkDropListDropped) = "drop($event)" cdkDropListOrientation="horizontal"> 2- on the draggable element we r going to create a directive with a hostlistner : <div cdkDrag appDropzone (received)="onDrop(elementcdkDrag)" > 3- on component.ts we create our onDrop method, to store the data in a variable called 'droppedOn': onDrop(elementcdkDrag: any) { console.log('droopedon', elementcdkDrag); this.droppedOn = elementcdkDrag; } 4- we override the drop method: drop(event: CdkDragDrop<any[]>) { const elementsToExchange: any[] = [this.yourArray[event.previousIndex].id, this.droppedOn.id]; if (elementsToExchange[0].id !== elementsToExchange[1].id) { // i rerender my grid here } } 5- finally here is my hostListhner: @Directive({ selector: '[appDropzone]' }) export class DropzoneDirective { @Output() received = new EventEmitter<element>(); constructor() { } @HostListener('mouseenter', ['$event']) onDrop($event) { $event.preventDefault(); this.received.emit($event.element); } }

hope it's useful

Dicairo avatar Aug 29 '19 07:08 Dicairo

I have the same problem with chip-list https://stackblitz.com/edit/angular-chips-pfmtqy The implementation its from angular/material examples with CdkDragDrop but its only for horizontal orientation. So if you have more than 1 line chips you cant drag n drop.

nicholasp avatar Sep 04 '19 09:09 nicholasp

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

I created a dynamic component based on this stackblitz example. https://github.com/sam-1994/ngx-drag-and-drop-grid

Currently it is only tested with Angular 8.x. Feel free to include it in your projects or giving feedback for improvements

I'm having problem with https://stackblitz.com/edit/angular-dyz1eb when i use ngIf to dynamically display 2 different list, but when i try to drag drop, the dropElement index in the dropElement list seems to increase. The bug only happen once each time i show different view. Your component seems to fix that issue, may i ask what exactly did you do to fix it? i'm investigating your code but can't find anything My issue is presented in this: https://stackblitz.com/edit/angular-yq6dww

giangtuan085 avatar Sep 25 '19 11:09 giangtuan085

@sam-1994

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

I created a dynamic component based on this stackblitz example. https://github.com/sam-1994/ngx-drag-and-drop-grid

Currently it is only tested with Angular 8.x. Feel free to include it in your projects or giving feedback for improvements

Hi Sam and thanks for your work; YOur solution is the only one working for me over Angular 8. can you please tell me which event we can use (similar to (cdkDropListDropped) ) because it s not written in the API.

robroy92 avatar Sep 29 '19 18:09 robroy92

https://stackblitz.com/edit/angular-dyz1eb

This works also with angular material 8. Thanks

and some more: 4. add implements AfterViewInit 5. all == and != should be === and !== 6. " should be '

thanks @c3rber i used your code to create a select candidate page: https://stackblitz.com/edit/angular-yysvkf

@c3rber How can we implement drag drop with multiple lists which have flex-wrap: wrap In the above example moving items from Matched to Kandidaten list

vankadn avatar Oct 03 '19 20:10 vankadn

ck for improv

@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb

I created a dynamic component based on this stackblitz example. https://github.com/sam-1994/ngx-drag-and-drop-grid

Currently it is only tested with Angular 8.x. Feel free to include it in your projects or giving feedback for improvements

Hey Sam how to have multiple sam-drag-and-drop-grid and drag between them

vankadn avatar Oct 03 '19 20:10 vankadn