ember-drag-sort: 如何确定拖拽列表中的新父级?
ember-drag-sort: how to determine the new parent in the dragged list?
最初由 @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?
解决方案 0:调整您的数据模型以将 isParent
属性转换为派生值而不是真实来源
具有必须手动更新的 isParent
属性首先是一种有缺陷的方法。
如果您将 isParent
状态作为属性并要求前端对其进行更新,那么您将拥有两个可能(并且最终会)不同步的真实来源。特别是考虑到用户可以篡改对 API 后端的网络请求。
isParent
应该是从children的数量推断出来的。它可以是一个简单的计算 属性:
{
isParent: computed('children.[]', function () {
return this.get('children.length') > 0
}
}
可以在后端使用类似的方法。
如果您不控制后端并且仍然需要从前端更新 isParent
属性,我建议您修改序列化程序以包含 isParent
计算的 属性 在序列化期间将值放入有效载荷中。
虽然我坚信您应该采用此解决方案,但我已经研究了以下几个备选解决方案。
解决方案 1:使用观察者自动更新 parent 状态
在您的模型中:
{
updateParentState: Ember.observer('children.[]', function () {
const isParent = this.get('children.length') > 0
this.setProperties({isParent})
})
}
这将使 isParent
属性在更新时与其 children
关系保持同步。
这是一个演示:https://ember-twiddle.com/f1c737d3bc106cb9cca071fd01fe334f?openFiles=models.item.js%2C
请注意,如果您在拖动结束时自动保存记录,则应将保存包装到 Ember.run.next
中,以便在观察者触发 之后进行保存。
方案二:访问被拖项目的新旧parent
鉴于您的关系设置如下:
export default Model.extend({
isParent: attr('boolean'),
parent: belongsTo('item', {inverse: 'children'}),
children: hasMany('item', {inverse: 'parent'}),
})
...您可以在拖动结束动作中访问被拖动项目的新旧parent!
{
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) // <--
},
}
}
我用箭头注释标记了相关行。
看,您在移动之前从拖动的项目中读取了旧的 parent。移动项目后,您会阅读新的 parent。这是可能的,因为 Ember 数据自动执行关系簿记。
最后,您更新两个 parent 的 isParent
状态。
最初由 @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?
解决方案 0:调整您的数据模型以将 isParent
属性转换为派生值而不是真实来源
具有必须手动更新的 isParent
属性首先是一种有缺陷的方法。
如果您将 isParent
状态作为属性并要求前端对其进行更新,那么您将拥有两个可能(并且最终会)不同步的真实来源。特别是考虑到用户可以篡改对 API 后端的网络请求。
isParent
应该是从children的数量推断出来的。它可以是一个简单的计算 属性:
{
isParent: computed('children.[]', function () {
return this.get('children.length') > 0
}
}
可以在后端使用类似的方法。
如果您不控制后端并且仍然需要从前端更新 isParent
属性,我建议您修改序列化程序以包含 isParent
计算的 属性 在序列化期间将值放入有效载荷中。
虽然我坚信您应该采用此解决方案,但我已经研究了以下几个备选解决方案。
解决方案 1:使用观察者自动更新 parent 状态
在您的模型中:
{
updateParentState: Ember.observer('children.[]', function () {
const isParent = this.get('children.length') > 0
this.setProperties({isParent})
})
}
这将使 isParent
属性在更新时与其 children
关系保持同步。
这是一个演示:https://ember-twiddle.com/f1c737d3bc106cb9cca071fd01fe334f?openFiles=models.item.js%2C
请注意,如果您在拖动结束时自动保存记录,则应将保存包装到 Ember.run.next
中,以便在观察者触发 之后进行保存。
方案二:访问被拖项目的新旧parent
鉴于您的关系设置如下:
export default Model.extend({
isParent: attr('boolean'),
parent: belongsTo('item', {inverse: 'children'}),
children: hasMany('item', {inverse: 'parent'}),
})
...您可以在拖动结束动作中访问被拖动项目的新旧parent!
{
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) // <--
},
}
}
我用箭头注释标记了相关行。
看,您在移动之前从拖动的项目中读取了旧的 parent。移动项目后,您会阅读新的 parent。这是可能的,因为 Ember 数据自动执行关系簿记。
最后,您更新两个 parent 的 isParent
状态。