Originally asked by @andrewsyd on GitHub.
For a nested list, is there a way to determine in the
dragEnd
action which "sub-list" the item has been dropped into? (e.g. a class name, id etc)In my scenario, I am sorting ember data records that can belong to each other (i.e. a nested, 'tree' structure). When I drag one nested record "into" another (making the dragged record a child of the second record), I need to update the parent attribute in ember-data. My question is, how do you pass some id of the second record (the new parent) to the
dragEnd
action?Is this even possible?
isParent
attribute into a derived value rather than a source of truthHaving an isParent
attribute that must be updated by hand is a flawed approach in the first place.
If you have the isParent
state as an attribute and require the frontend to update it, then you have two sources of truth that can (and eventually will) go out of sync. Especially so given the fact that users can tamper with network requests to your API backend.
The isParent
should be inferred from the amount of children. It could be a simple computed property:
{
isParent: computed('children.[]', function () {
return this.get('children.length') > 0
}
}
A similar approach can be used on the backend.
If you don't control the backend and still need to update the isParent
attribute from the frontend side, I recommend that you hack your serializer to include the isParent
computed property value into the payload during serialization.
Though I strongly believe you should go with this solution, I've researched a couple alternative solutions below.
In your model:
{
updateParentState: Ember.observer('children.[]', function () {
const isParent = this.get('children.length') > 0
this.setProperties({isParent})
})
}
This will keep the isParent
attribute synchronized with its children
relationship whenever it's updated.
Here's a demo: https://ember-twiddle.com/f1c737d3bc106cb9cca071fd01fe334f?openFiles=models.item.js%2C
Note that if you automatically save your record(s) on drag end, you should wrap saving into Ember.run.next
, so that saving happens after the observer fires.
Given that you have relationships set up like this:
export default Model.extend({
isParent: attr('boolean'),
parent: belongsTo('item', {inverse: 'children'}),
children: hasMany('item', {inverse: 'parent'}),
})
...you can access the old and new parent of the dragged item in the drag end action!
{
actions : {
dragEnd ({sourceList, sourceIndex, targetList, targetIndex}) {
if (sourceList === targetList && sourceIndex === targetIndex) return
const draggedItem = sourceList.objectAt(sourceIndex)
const oldParent = draggedItem.get('parent') // <--
sourceList.removeAt(sourceIndex)
targetList.insertAt(targetIndex, draggedItem)
const newParent = draggedItem.get('parent') // <--
newParent.set('isParent', newParent.get('children.length') > 0) // <--
oldParent.set('isParent', oldParent.get('children.length') > 0) // <--
},
}
}
I've marked relevant lines with arrow comments.
See, you read the old parent from the dragged item before moving it. After you move the item, you read the new parent. This is possible because Ember Data performs relationship bookkeeping automatically.
Finally, you update the isParent
state of both parents.