Closed jelbourn closed 6 years ago
Is there any design doc I can follow @jelbourn ? I've tried quite a few drag and drop libraries, all of the are bad in one way or another, those that use native APIs are rather limited when it comes to styling the content (which is also often glitched when you drag an item that is partially hidden behind overflow) that is being dragged and those that do it programatically often don't have must have accessibility features like scrolling when the item being dragged is on the side of the scrollable element, so I really wonder how you gonna tackle this.
Kristiyan is working on a design now. Any specific feedback you have about what you like/dislike about other libraries would be good to know.
Most of the libs (with native html dnd) such as angular-dnd, sortablejs, dragula handle DOM on their own which is very inconvinient when using unidirectional dataflow (ngrx). There is no an easy way to cancel these updates and walkarounds feel very hacky. This is the biggest issue with all the libs I've used so far I would love to see the following features:
Would be awesome if we would get something like https://github.com/atlassian/react-beautiful-dnd
Additionally, most seem designed to simply append elements onto lists of other elements when dropped into a drop zone. I would love to see a "canvas" mode implemented to simplify the dropping into an absolute position of an element. This would be extremely useful for design oriented apps, where the user is building something from smaller elements. I used interactjs years ago and really appreciated all it's features ( touch support, inertia, grid snapping, svg support ). It's an older library but could prove useful at least for feature ideas. I'm ready to contribute in any way I can once you all have laid the plans and groundwork.
As mentioned dojchek full support for state management like ngrx/redux is very important. We don't yet have an angular dnd library that does.
What would be an example of a scenario w/ ngrx interplay with drag-and-drop?
Not sure if this is quite the example you're looking for but I have a site that I maintain that uses drag and drop to create a data sorting functionality. We built it to be closely tied into ngrx where each of the containers (items to be sorted and a container for each category is populated via ngrx store selector. When the user drags an item from one list to another, we cancel all the default drag/drop actions and instead fire a ngrx event that updates the object in question with the new category and let the container selectors "move" the item (this allows for some other things like firing save events and such).
Having something built into the cdk that could facility the drag/drop actions and allow me to intercept at certain events (ie. onDrop) to do things like fire ngrx action and cancel any further drag/drop effects would be great.
I've ported an angularjs library but intentionally just implemented drop zones to be as general purpose as possible. By having a general purpose drop zone all the ngrx use cases should be possible as well because the library just informs you something has been dropped. What should happen with that event is all up to the developer.
https://github.com/reppners/ngx-drag-drop
So if you're asking for features I'd like to see for CDK than all of what the mentioned library does because it's quite basic (does not do the lists or sorting stuff internally) but configurable to cater all use cases.
Wether it's implemented with native HTML5 drag-and-drop or implementing it all in the angular stack is up to you, but what I'd like to be possible is drag stuff from the outside of the browser into an app. This is just possible with native drag-and-drop.
It's probably important to come up with a list of drag, and drag and drop related use cases. What sort of things are we dragging? What sort of drop targets are we going to have? I suspect there's probably more permutations of the latter: single drop targets, groups of drop targets, nested drop targets. There's also a variety of ways that you might want to execute logic when interacting with those targets while dragging or dropping.
ngrx
FWIW, I wouldn't worry about Redux or ngrx when it came to current drag state. That's really a UI concern, and not something I would do as critical to persist to state.
Interesting library written in ES6 Drag and Drop lib
If we could have this: https://github.com/react-dnd/react-dnd for angular, I would be very satisfied.
I am working on drag and drop in an Angular app this week. @dojchek already mentioned most of the features required for my use case, but here are a couple more:
Would be great!
On our teams wish list:
body
during the dragging, so it escapes hidden overflows etc). Maybe support providing a ng-template
for it?Another trick/feature is to have the center of the ghost element as reference for the drop behaviour (instead of the pointers position). E.g. you start dragging an element by "grabbing" it at its left side, and when the center of the dragged element covers the dropzone, but your pointer is not hovering it, you still want to be able to drop it in the dropzone?
By the way. When sharing ideas, why not sharing Stackblitzes? https://draggable-with-anchors-lucas-8eq4ht.stackblitz.io 😄
Today I found some time to work out some ideas. Please see: https://stackblitz.com/edit/draggable-system
What is in it:
The directives:
Just wanted to drop my ideas here...
@dirkluijk It it supposed to be interactive? I had a look and nothing was draggable, is it supposed to be?
Oops, forgot to add polyfill for pointer events. I guess you checked on a mobile browser. Should work now!
Nah just Firefox, seems they don't have it enabled by default yet.
On Feb 16, 2018 6:02 PM, Dirk Luijk notifications@github.com wrote:
Oops, forgot to add polyfill for pointer events. I guess you checked on a mobile browser. Should work now!
— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/angular/material2/issues/8963#issuecomment-366383519, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AF5opZI0f_njRwOL1dNdty2Guf1qVYD5ks5tVgkbgaJpZM4Q_jF0.
Firefox worked for me. @dirkluijk Nice work! That looks better than most Angular drag and drop libraries I have found. I'm happy to see that we have so many folks chompin' at the bit to contribute to this. I think this project will mature rapidly.
Here is a second version with some droppable ideas demonstrated: https://stackblitz.com/edit/draggable-system-2
draggableData
can be set which will be included in the custom events emitted. @dirkluijk looks like the polyfill you added didn't stick, it works great when I do add the polyfill though!
@Tiedye My mistake. Updated both Stackblitz demos!
I didn't see this one mentioned anywhere: http://kamilkp.github.io/angular-sortable-view/
Few forks down it works ok for our use cases - I had to implement auto scrolling on any number of scroll-able parents though and fix it on mobile on latest chrome (thanks to passive flag). It is also working nicely in IE 11. On the other hand the version we have in the project is cleaned up a bit more and I think I could make it public if it would help anyone.
Sadly it's only available for angularjs and doesn't seem to be maintained. Maybe some ideas / code could still be reused? It is fully declarative after all. That aside, I have to find / make an alternative as well as we are in the process of migrating. Personally I'd would try to rewrite that - as others outlined, there aren't that many good ones out there.
I think it would be a good idea to wait and see what @crisbeto has in store, how far in development / design process he is.
@crisbeto in case there is no progress on this feature, I've refactored angular-sortable-view to typescript and made it work with angular 5 (and angularjs) I could try to push it and see if we can contribute.
I have a little prototype over here. If you want to play with it I suggest doing so not on a smartphone since I didn't put much effort into the styling of the demo app and it is not mobile friendly.
I am planning on releasing an early version of an Angular library next week. Based on https://stackblitz.com/edit/draggable-system-2.
Of course I hope its ideas will be valuable for a CDK package.
@dirkluijk, Looking forward!
Very nice work @dirkluijk
@dirkluijk Anything you can show us? Eager to see some demo of how this is going to work!
Hello all,
Inspired by @dirkluijk and others, I have put together the following library:
https://github.com/cengiz-dev/angular-drag-and-drop
Demo:
https://stackblitz.com/github/cengiz-dev/angular-drag-and-drop
Let me know what you think.
Cheers
My most recent work is visible on
https://stackblitz.com/edit/draggable-part-5 and https://stackblitz.com/edit/draggable-part-6.
It is the code behind this tutorial: https://www.youtube.com/playlist?list=PLJpL6ImpHi2i1ALq7-x7zRADoole7eq8U.
Next challenges:
I was planning on extracting to a library as well
@cengiz-dev will take a look at your code as well. Expect my feedback in a day or two.
@dirkluijk your example6 sucks with relativly big list (even with virtual scrolled) https://stackblitz.com/edit/draggable-part-6-ovo5rl?file=app/app.component.ts
Whoa, easy. It does not suck, but wasn't yet optimised. Please see this example where it performs a lot better: https://stackblitz.com/edit/draggable-part-6-ycchkt?file=app%2Fapp.component.html
The trick is to handle all pointermove
logic outside Angular (using ngZone.runOutsideAngular()
). This is a known trick which is used inside Angular CDK as well.
To give an update on the rest of the drag/drop ideas: I am currently struggling with connected sortable lists. The idea is that you can move elements between multiple lists on the fly, e.g. with a single drag operation.
Of course, I can just destroy the old element (view) and create a new one using Angulars ngFor
directive. However, in combination with dragging, this is quite hard. On Safari this won't work with the pointer events polyfill because the browser stops emitting pointer events when the original element is destroyed. Also, it would be nice if the original draggable directive remembers its state when moved to another location accross the DOM.
So...
I am looking for a clean way to render multiple lists with a single ng-template
and to move views accross multiple view containers. This can be quite hard, because you would have to reuse IterableDiffer
logic which is used by Angulars ngForOf
and combine it with multiple iterables.
For example: when change detection occurs, and iterable 1 has an item deleted and interable 2 has an item added, this means that the original view should be reused and a ViewContainer.detach()
should be performed together with a ViewContainer.insert()
.
@istiti criticism should always be constructive, not vague, negative and lazy.
@dirkluijk identified a need many in this community have and has shared his solution as well as offered instruction on how build your own implementation, a rare gift and sign of the strength of this community. Legitimate issues with his solution should include specifics ( at what size does it start to suck? ), and hopefully some proposed solution.
Adolescent insults towards contributions is destructive to our community and will not be tolerated.
Nice progress @crisbeto . Checked the demo and found a minor glitch. If the handle is released without moving the element (like a double click for example) sometimes the preview element freezes and the item cant be dragged anymore. Trying to find out why.
Is there a plan to support sorting nested lists both with vertical and horizontal layout? Tried to create a horizontal list of items using float and tried it with flex layout, but sorting is not working properly. I would like to create a kind of configurable dashboard something like this:
@tibortotok https://github.com/angular/material2/pull/12104 got in a couple of hours ago which adds support for horizontal sorting.
Thanks @crisbeto, that sounds great! :)
My newest ideas: https://stackblitz.com/edit/draggable-release-candidate
I finally managed to get the connected sortable list working. Still have issues on Safari. What I like of this approach is that the helper is an actual ng-template
so that you can customize its appearance and content.
@dirkluijk Thanks for the good work! Just for feedback, I noticed the connected sortable list sometimes duplicates a block when dragging from one list to the other. Tested on mobile Android 8.1.0 (so touch) with Chrome 67.0.3396.87.
@crisbeto, some feedback on the current cdk-experimental/drag-drop
, which may or may not be useful to you:
The use of @Input() connectedTo: CdkDrop[];
for linking drop zones isn't the easiest API to work with. For example, say I have a large list of volunteers and a large list of roles the volunteers can be assigned to (both rendered with ngFor
).
Currently, I think you need to use @ViewChildren(RoleEntry) roleEntries
in the parent component to grab all the role entry components and provide them as a [connectedTo]="roleEntries?.toArray()"
input to the volunteer list (CdkDrop) component.
The main problem is that, since the role drop zones are created with ngFor, you cannot pass them as an array to the connectedTo
input of the volunteer's list using a template reference variable.
A more user developer friendly solution might be to assign a string label to each drop zone. basically allowing something like "this volunteer entry (i.e. draggable) can be dropped on a zone labelled 'role' OR a zone labelled 'volunteer' (<-- for reordering) OR a zone labelled 'shift'". I think the ng2-dnd
library makes use of a strategy like this.
The fact that CdkDragHandle
must be a ContentChild
of CdkDrag
can be...annoying. Say I have a custom list component <my-list></my-list>
and I have several different custom list entry components (<my-person></my-person>
, <my-pet></my-pet>
, etc) that can go within it. I may use the components like so (assuming <my-list>
extends CdkDrop
)
<my-list>
<my-person *ngFor="let person of people" [person]="person" cdkDrag></my-person>
</my-list>
<my-person>
already has, within its view, its own list of context buttons. I'd like to place the CdkDragHandle on one of those buttons, but CdkDrag
wants me to do this:
<my-list>
<my-person *ngFor="let person of people" [person]="person" cdkDrag>
<button mat-icon-button cdkDragHandle><mat-icon>reorder</mat-icon></button>
</my-person>
</my-list>
If there are many instances of <my-list><my-person></my-person></my-list>
in an app, needing to insert the cdkDragHandle
as ng-content
can get annoying.
A workaround is to use @ViewChildren(CdkDragHandle)
within <my-person>
to grab the CdkDragHandle
from within the view of <my-person>
, then inject the instance of CdkDrag
into <my-person>
, then manually set the CdkDrag _handles
property equal to the ViewChildren
query. Something like:
@ViewChildren(CdkDragHandle) _handles: QueryList<CdkDragHandle>;
constructor(
@Inject(CdkDrag) @Self() @Optional() public drag: CdkDrag,
) {}
ngAfterViewInit() {
this.drag._handles = this._handles;
}
This strategy isn't ideal.
Currently, it's not possible (at least easily) to drop a draggable onto a drop zone and not transfer the draggable. Sometime's I'd like to simply use a drop to pass hidden data / trigger an output event while returning the dragged component to its original position.
In the same way that CdkDrop
provides an @Input() data: T
property for arbitrary data, it would be nice for CdkDrag
to have an @Input() dragData: T
property for arbitrary data.
Anyway, I realize that DragDropModule is a work in progress. It seems very likely some or all of these changes are already planned, but figured I'd call them out now as sore spots regardless.
Thanks!
So far I have only one problem with cdk-experimental/drag-drop
and that is that it doesn't scroll the parent element with overflow: auto;
so you have to drag, drop at the top, scroll, drag, drop at the top..
@thefliik:
For issue 1 maybe it's better to have some kind of "connectedGroup" parent directive to connect multiple directives?
For issue 2: I recognise that issue from the project where we built our own d/d solution. The limitation is that @ContentChildren does not query inside child component views. It could be solved by reversing the dependency: the CdkHndle gets the CdkDrag injected and triggers it. But in my solution this was not very easy to refactor because of.. several reasons.
What about MyPersonComponent extends CdkDrag
? Then it is much easier to connect things. As a matter of fact, every <my-person>
should aways be draggable, since you embed a handle in its view. ;) If not, then the handle should be external, or part of its content.
Depending on extends
on a component level in a library is normally not a good idea because the library consumers might want to extend their own classes for the reasons that are way bigger than just drag and drop. Then usually it becomes a pain to choose what of those 2 parent classes to extend which in the end will make people crying
@dirkluijk I think the solution to issue 2 is to have CdkDragHandle inject the parent CdkDrag directive and register itself as a handle with the parent. This way, both use cases (i.e. View and Content) are accommodated. e.g.
export class CdkDragHandle {
constructor(
public el: ElementRef<HTMLElement>,
@Inject(forwardRef(() => CdkDrag)) private dragDirective: CdkDrag,
) {}
ngOnInit() {
this.dragDirective._handles.push(this);
}
}
I believe this strategy would (ahem) drop (😃) easily into the current CdkDrag
implementation, which doesn't check it's handles until dragging begins.
Regardless of how it's done though, I'm confident in the material team's abilities to solve this annoyance if they decide they wish to do so.
What about MyPersonComponent extends CdkDrag?
It's already extending another class :-/ (a.k.a. @smnbbrv is exactly correct). But in general you are right, "every <my-person>
should aways be draggable."
Regarding issue 1, I imagine there are a number of different approaches and I'm not particularly invested in any strategy other then to call out the fact that the current api is lacking. I'd probably start by trying to give each droppable a label (which I suppose could be a symbol, if strings make folks uncomfortable) and registering them with a service that can automatically connect them based on the labels. Labels seem like the most developer friendly approach while still being flexible, but I'm sure @crisbeto
has given this a lot more thought.
Regarding issue 1, something like this?
<!-- draggable with group 'foo' -->
<div cdkDrag [cdkDragGroup]="'foo'"></div>
<!-- dropzone which accepts 'foo' and 'bar' -->
<div cdkDropzone [cdkDragGroups]="['foo', 'bar']"></div>
Regarding issue 2: sounds ok.
However, I must say that having interaction between a directive outside a component with a directive inside a component feels somehow a bit dirty. Shouldn't they both live either in the same component, or outside it (for example in ng-content
)?
e.g.
<!-- app.component.html -->
<foo-component cdkDrag>
<div cdkDragHandle></div>
</foo-component>
I am not sure.
The current version cdk-experimental/drag-drop does not work well with flex-layout
P/S Maybe it makes sense to make all the issues in "Projects" as with virtual scroll
Having refactored angular-sortable-view to angular directives internally and seeing the discussion lately here, it's quite clear that people want something that was done before.
Maybe it would still be worthwhile to check their api and try to cover the functionality described there? Custom helpers, placeholders, groups, auto scrolling are all in there and the same pattern works well as angular directives with no need to extend custom classes (I am surprised to see this actually).
I'm fairly sure I could convince my manager to allow me to open source this internal lib and continue to contribute if it could become part of cdk as this is what we rely on and are quite happy with (overlays and tables are great!)
@FDIM I imagine people would find value in that. Personally, I've already found a DnD library I'm quite happy with. I'm providing feedback not because I need these features now, but because I'd like to see CdkDragDrop succeed.
The library I'm using is AngularSkyhook, an implementation of the excellent react-dnd library (apparently used by Trello) to angular. Apparently, because the core of react-dnd was implemented as pure javascript (i.e. react-dnd-core), and doesn't even make use of the DOM, the library is particularly portable to many environments. Some examples of what AngularSkyhook can do (all written in Angular).
It's definitely a lower level library then what many may want (for instance, it does not come packaged with a sortable directive), but it has the benefit of relying on a very mature base and I'm finding it to be very flexible.
That looks pretty good. How much does it add to the bundle size?
This issue tracks the high-level addition of drag-and-drop features to the cdk.