valor-software / ng2-dragula

Simple drag and drop with dragula
http://valor-software.com/ng2-dragula/
MIT License
1.91k stars 430 forks source link

Reactive Forms integration? #709

Closed riyunoa closed 7 years ago

riyunoa commented 7 years ago

I'm getting this error "core.umd.js:485 ERROR TypeError: Converting circular structure to JSON" when dragging between 2 bags that contain FormGroup objects.

<div [dragula]='"my-bag"' [dragulaModel]='items'>

i.e. the "items" array contains Angular FormGroup objects

Help! I'm using 1.3.1

cormacrelf commented 7 years ago

Sorry, can't help you. Look at https://github.com/valor-software/ng2-dragula/blob/master/src/components/dragula.directive.ts. ng2-dragula doesn't interact with forms at all.

However, I can recommend using a good quality debugger (Chrome) and setting breakpoint on errors, and walking up and down the call stack. To me the error looks like you've got a FormGroup that's got a circular reference in there.

Circular as in:

let a = {}
a.someprop = a;

Trying to JSON serialize that is impossible. Clearly either some of your code or the forms module code is trying to do that.

cormacrelf commented 7 years ago

Edit: https://github.com/valor-software/ng2-dragula/blob/master/src/components/dragula.provider.ts#L91 does do JSON.stringify, I should have known. Perhaps still you are creating circular FormGroups; perhaps FormGroups are inevitably circular by design and can't be used with dragulaModel. Either way, it's probably better to isolate the formgroup creation into another component, and pass around simple model objects instead. Then you'd go:

<div class="container" [dragula]="'bag'" [dragulaModel]="simpleModels">
  <form-component *ngFor="let m of simpleModels" [model]="m"></form-component>
</div>

... and create a FormGroup from a simple model object in the constructor/init of your form-component.

riyunoa commented 7 years ago

Thanks @cormacrelf , that's what I ended up doing in the end. I do think Angular 2 FormGroup and FormControl are circular because just doing json encode gave me the same problem. I had to do myFormGroup.value to get the value of it, but I don't think that would really work with ng2-dragula as I didn't want to re-instantiate the FormGroup every time I dragged and dropped.

Is providing support for reactive forms in the pipeline at all?

cormacrelf commented 7 years ago

I wouldn't call it support or integration but if there's a way to deep copy and maintain circular references, maybe.

Why would you want to pass around a form control? Do you want to preserve the pristine/etc properties?

nayfin commented 6 years ago

Support for forms API would be great. I need to be able allow the user to reorder a FormArray. I wouldn't think Dragula would have to maintain /update the form status. If I am able to make any headway without support, I'll document it here for anyone else interested.

cormacrelf commented 6 years ago

@nayfin this is a by-design flaw. 'Support' is the wrong word. Whatever hack gets it to work is probably worthless compared to actually designing this thing again. If you can, good, but probably mark that code for removal in favour of a better-designed ng2-dragula from the start.

nayfin commented 6 years ago

@cormacrelf Is a rebuild in the cards? I know this isn't a discussion board but I am trying to decide which ng drag/drop library is best suited to my purposes and this will be an influencing factor. I would also be happy to contribute in any way I can.

cormacrelf commented 6 years ago

@nayfin for now, probably do this:

  1. give dragulaModel a list of indices [0,1,2,3,4] that match ones on the formcontrols/groups
  2. store forms in a map instead
  3. let dragula handle reordering indices
  4. subscribe to dropmodel; build the form array again from that list of indices each time it changes, if you need it at all: formArray = newIndices.map(i => formMap[i])

Alternatively, eschew dragulaModel altogether like I did, use data-attrs & read 'em in the handlers, or literally keep track of which nativeElements map to which part of your array, and do the array splicing yourself. (It's not hard. Read the code in this or just play around with Array.prototype.splice.) I did it because I use immutable lists, and ng2-dragula doesn't know how to filterNot(...).insert(...) or indeed fire Redux actions that do it.

I take back '[w]hatever hack gets it to work is probably worthless compared to actually designing this thing again'. Either way you get around this problem, it's better architecturally not to offload updating your data model to an unmaintained, destructive-mutative-only dragula shim.

And yes, I will rewrite (or even just delete! whew!) the dragulaModel code when I get assigned back to building the drag/drop stuff I had going, or when I get time.

cormacrelf commented 6 years ago

@nayfin I would like to hear your thoughts on some other drag/drop libraries... About a year ago there were no other libs that did it. angular4-drag-drop looks very, very limited, can't see any others. If only someone could create angular bindings for... react-dnd... such clean API

nayfin commented 6 years ago

For my sorting problem I found angular-sortablejs which supports the FormArray natively and was stupid easy to implement. Thank you for your insights though, they were very helpful.

As for my drag/drop background. My first web app was a, now defunct, drag and drop garden planner I built almost 3 years ago. I started with interact.js then switched to jQuery UI. I have been preparing to rebuild it in Angular for some time now, but I have been focused on mastering Angular and outlining the structure first. I am almost ready to start playing with the drag and drop aspect of it, which is why I reached out.

Looking back I really like the interact library. Touch and multi-touch capabilities are baked in, as well as momentum and resizing. I have no clue what it would take to create something similar for angular, but it would be fun to figure out. One major hurdle that I see is dragging from one element into another.

A friend recently pointed me towards the greensock library as well, but after reading through a bunch of forums it doesn't seem like it plays well with Angular.

If you do start down this path we should set up some other channel of communication, so we aren't using this issue as a chat forum. Thanks for the support and interest, I'm keen to hear what you find and learn.

cormacrelf commented 6 years ago

I dunno about ‘starting down this path’... I stayed up late yesterday and actually finished integrating Angular with react-dnd, which is IMO the greatest dnd library out there by MILES. The original lib is in 3 layers - html5 backend, dnd-core and react-dnd. I reimplemented the third one; all it has to do is supply real DOM elements (via directives instead of React HoC) to the other layers and have some way of responding to callbacks, again from the other layers.

If your needs were handled by sortablejs, then don’t go any further, but people post issues all the time on here about very complex use cases that make so much more sense being handled without any truth in the DOM.

Read the introduction blog post and the overview and then the examples on the latter page to see what it’s about.

cormacrelf commented 6 years ago

And for completeness to this thread there is one other remotely worthwhile drag/drop library: ng2-dnd aka ngx-dnd. I think it aims at the complex scenarios handled easily by react-dnd, and has some attempt to separate data from the dom. But since it only does that for the ‘dragData’ (react-dnd ‘item’) and not the actual dragging state, looking at the GH issues, people are still struggling with basic things like rendering different things on drop targets while in transit (you have to use CSS classes). My solution (which I hope to publish soon) is better. In that scenario you have an Observable<DropTargetMonitor>, which mirrors this; map that observable to what you’re interested in, and use that with |async in your template.

nayfin commented 6 years ago

Thanks for the reading. I can appreciate his approach but I can't read react well enough to understand how he's implementing it. Please keep me in the loop with your progress, I'm interested to see what it looks like in Angular. I'm happy to contribute however I can.

cormacrelf commented 6 years ago

@nayfin FYI, I went through with it. It works pretty well. https://github.com/cormacrelf/angular-skyhook