components
components copied to clipboard
Drag Drop Sortable, mixed orientation support
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
@jelbourn hey ,have any progress about this issue now?
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
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 +1 , i want to know how to implement what the demo shows
@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
Would be nice if what's shown in the demos was actually backed up by the framework code.
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:
Any progress about this issue?
Almost a month has passed , any update now ?
Any progress about this issue?
@MrSnappingTurtle Do you have demo code of multiple list wrap? Thanks :)
Although i know to achieve this feature is very hard. but I still want to ask "any progress about this issue?"
Is there any progress on this one? Or eventually any idea of when it would be implemented?
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.
for time being you can take a look this - https://github.com/smnbbrv/angular-sortablejs-demo/tree/master/src/app/examples/multiple-lists
Thank you! I appreciate your help, I'll check it out.
Best regards, Flo
here is a workaround with using multiple lists https://stackblitz.com/edit/angular-nuiviw
@c3rber That was wonderful! You are my best friend! Thanks a lot ! i will try it.
@c3rber Wow, it's work for me, thx.
But.... Any OFFICIAL progress about this issue? It's for make and standar to this.
Thx
@c3rber Yeah, it is working fine.
Is there a way of animating @c3rber workaround?
@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 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 GJ man! It's okey!
I'm going to try it in my application
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 .
@c3rber Nice solution, although it doesn't work quite well on divs with different width-sizes. Any solution for this?
@crisbeto This issue should labeled "critical" as most use-cases with responsive design need multi-line support.
@c3rber Fantastic solution. This is exactly what we were looking for.
Last year , I subscribe this problem , today i hope this problem could be solve in 2019. waiting for it.
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.
Note: obviously, you'd have to also include material package and correct module.
@skatestyle Do you have the same version of the cdk installed as in the demo (7.1.1)?
@devtician Nope, I am currently using 7.2.0
@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 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.
@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.
@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 π
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 handlerentered
uses for rearranging the list - sometimes the rearranging action doesn't work (see below)
![image](https://user-images.githubusercontent.com/14309096/52032339-12d56900-2553-11e9-8ab0-a828ea4af0d0.png)
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.
We need this too.
@krusli works perfectly, thank you π
@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 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 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..
I'm using dnd2 now. Hope to have the official solution
@qichangjun hm, but it looks like it isn't supported ( https://github.com/akserg/ng2-dnd/issues/274
@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 im using cdkDrag for rearrange, grabber and resizable (custom) directives r used for resize.
@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).
This would be nice to have it sorted soon. We also need it.
I have found this incredibly interesting project here: (has no 3rd party dependencies).
https://github.com/swiety85/angular2gridster
@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 Thank you. Your suggestion helped me reduce some code π
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);
}
@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 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 thanks for the help.Let me try.
@crisbeto @devversion @jelbourn Gentleman, is this issue planned to be fixed this year ?
Sincerely, Devs.
Nevermind Best alternative: https://github.com/valor-software/ng2-dragula
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?
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 (
Please help.
We need this too.
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.
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 thancdkDragEntered
, 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);
}
Hi ! Thanks @Francois-Belanger for your solution, it works but visual glitches are annoying Is there any progress about this issue ?
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.
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
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.
@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 yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb
@c3rber cool.
can you change this: parentNode parentElement to avoid the compile error (node somehow does not have children defined)
@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
@tibistibi sure, have changed to parentElement @aryaroudi thanks, didn't notice. replaced with CdkDropList
@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 :)
@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
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 yes, but not sure that this will work well on touch screen devices. I have replaced undocumented private methods with local ones.
great job @c3rber π
@c3rber good point, i will investigate and thanks for improving the code!
@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:
- change let to const
- add {} for all if's
- make functions private methods
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 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)
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
@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
@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb
THANK YOU SO MUCH
Could someone do a npm package for this, so it could easily be integrated ?
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.
@crisbeto will this bug be fixed in next release of CDK, planned for Q1 β Q2 2019 (January - June)?
@masaala please post a stackblitz. @seriojak its not a bug, its just a very important feature that hasnt been covered yet.
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.
https://stackblitz.com/edit/angular-dyz1eb
This works also with angular material 8. Thanks
@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb
Cool
@tibistibi yes, here is updated code for 7.3.7 https://stackblitz.com/edit/angular-dyz1eb
Thanks a lot!
Is there any official solution to make it with mat-grid-list??
Can we add a critical label on this issue as it is one of the most requested features?
@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
@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. :-)
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
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.
@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
@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.
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
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