如何使用 SortablejS 和 acts_as_list gem 处理多个列表之间的拖放?
How to handle drag and drop between multiple lists using SortablejS and acts_as_list gem?
我一直在寻找解决方案一天半。
我有几个 tables(类别),允许用户拖放每一行(项目)以重新排序每个项目在该 table(类别)上的位置。我正在使用 SortableJS 来处理拖放,并且我拥有它,因此可以在同一类别中重新排序。
在我的后端,我有一个 Rails API,它使用 Jbuilder 来 return JSON,我正在使用 acts_as_list gem 来处理我的职位。
当我将项目从类别 A 拖到类别 B 时,我无法弄清楚如何处理重新排序。我认为问题在于控制器操作以及我无法提出接收更新的解决方案对于多个类别,然后 return 使用 Jbuilder 更新类别的位置。如果能提供帮助,我们将不胜感激!
ItemsList.vue
<script>
methods: {
async dragReorder({ item, to, from, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, clone, pullMode }) {
// item that was dragged
const draggedItem = JSON.parse(item.getAttribute('data-item'));
// initial payload
const payload = {
category_id: draggedItem.category_id,
category_item_id: draggedItem.pivot.id,
item_id: draggedItem.id,
new_index: newIndex + 1,
user_id: this.user.id,
};
const newCategory = JSON.parse(to.getAttribute('data-category'));
// if item is dragged to new category
if (pullMode) payload.new_category_id = newCategory;
await categoryService.updatePosition(payload);
},
},
mounted() {
this.$nextTick(() => {
const tables = document.querySelectorAll('.items-table-container');
tables.forEach(table => {
const el = table.getElementsByTagName('tbody')[0];
Sortable.create(el, {
animation: 150,
direction: 'vertical',
easing: 'cubic-bezier(.17,.67,.83,.67)',
group: 'items-table-container',
onEnd: this.dragReorder,
});
});
})
},
</script>
category.rb
class Category < ApplicationRecord
has_many :categories_items, -> { order(position: :asc) }
has_many :items, through: :categories_items, source: :item
accepts_nested_attributes_for :items
end
categories_item.rb
class CategoriesItem < ApplicationRecord
belongs_to :category
belongs_to :item
acts_as_list scope: :category
end
item.rb
class Item < ApplicationRecord
has_many :categories_items
has_many :categories, through: :categories_items, source: :category
end
categories_controller.rb
def update_position
item = CategoriesItem.find_by(category: params[:category_id], item: params[:item_id])
# if item was moved to new category
categories = []
if params[:new_category_id]
item.update(category_id: params[:new_category_id])
Item.find(params[:item_id]).update(category_id: params[:new_category_id])
item.insert_at(params[:new_index]) unless !item
categories << Category.find(params[:category_id])
categories << Category.find(params[:new_category_id])
else
item.insert_at(params[:new_index]) unless !item
categories << Category.find(params[:category_id])
end
@categories = categories
end
update_position.json.jbuilder
json.array! @categories do |category|
json.(category, :id, :name, :created_at, :updated_at)
json.categories_items category.categories_items do |category_item|
json.(category_item, :id, :category_id, :item_id, :created_at, :updated_at, :position)
end
end
acts_as_list
允许您设置列表项的新范围参数和位置,然后只需保存该项目,该项目就会从旧范围移出并移入您所在位置的新范围自动希望(使用 after_save
回调)。
在这方面,您应该能够做到这一点,然后在您的两个范围中的每一个中重新获取项目,并将它们 return 到您的前端以更新您的显示。
终于有了一些工作,这是我的工作代码:
def update_position
@categories = []
## if item moved to new scope ##
if params[:new_category_id]
@category_item.update(category_id: params[:new_category_id])
@category_item.insert_at(params[:new_index])
@categories << Category.find(params[:new_category_id])
@categories << Category.find(params[:category_id])
## else if item was moved within same scope ##
else
@category_item.insert_at(params[:new_index])
@categories << Category.find(params[:category_id])
end
@categories
render :index
end
我一直在寻找解决方案一天半。
我有几个 tables(类别),允许用户拖放每一行(项目)以重新排序每个项目在该 table(类别)上的位置。我正在使用 SortableJS 来处理拖放,并且我拥有它,因此可以在同一类别中重新排序。
在我的后端,我有一个 Rails API,它使用 Jbuilder 来 return JSON,我正在使用 acts_as_list gem 来处理我的职位。
当我将项目从类别 A 拖到类别 B 时,我无法弄清楚如何处理重新排序。我认为问题在于控制器操作以及我无法提出接收更新的解决方案对于多个类别,然后 return 使用 Jbuilder 更新类别的位置。如果能提供帮助,我们将不胜感激!
ItemsList.vue
<script>
methods: {
async dragReorder({ item, to, from, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, clone, pullMode }) {
// item that was dragged
const draggedItem = JSON.parse(item.getAttribute('data-item'));
// initial payload
const payload = {
category_id: draggedItem.category_id,
category_item_id: draggedItem.pivot.id,
item_id: draggedItem.id,
new_index: newIndex + 1,
user_id: this.user.id,
};
const newCategory = JSON.parse(to.getAttribute('data-category'));
// if item is dragged to new category
if (pullMode) payload.new_category_id = newCategory;
await categoryService.updatePosition(payload);
},
},
mounted() {
this.$nextTick(() => {
const tables = document.querySelectorAll('.items-table-container');
tables.forEach(table => {
const el = table.getElementsByTagName('tbody')[0];
Sortable.create(el, {
animation: 150,
direction: 'vertical',
easing: 'cubic-bezier(.17,.67,.83,.67)',
group: 'items-table-container',
onEnd: this.dragReorder,
});
});
})
},
</script>
category.rb
class Category < ApplicationRecord
has_many :categories_items, -> { order(position: :asc) }
has_many :items, through: :categories_items, source: :item
accepts_nested_attributes_for :items
end
categories_item.rb
class CategoriesItem < ApplicationRecord
belongs_to :category
belongs_to :item
acts_as_list scope: :category
end
item.rb
class Item < ApplicationRecord
has_many :categories_items
has_many :categories, through: :categories_items, source: :category
end
categories_controller.rb
def update_position
item = CategoriesItem.find_by(category: params[:category_id], item: params[:item_id])
# if item was moved to new category
categories = []
if params[:new_category_id]
item.update(category_id: params[:new_category_id])
Item.find(params[:item_id]).update(category_id: params[:new_category_id])
item.insert_at(params[:new_index]) unless !item
categories << Category.find(params[:category_id])
categories << Category.find(params[:new_category_id])
else
item.insert_at(params[:new_index]) unless !item
categories << Category.find(params[:category_id])
end
@categories = categories
end
update_position.json.jbuilder
json.array! @categories do |category|
json.(category, :id, :name, :created_at, :updated_at)
json.categories_items category.categories_items do |category_item|
json.(category_item, :id, :category_id, :item_id, :created_at, :updated_at, :position)
end
end
acts_as_list
允许您设置列表项的新范围参数和位置,然后只需保存该项目,该项目就会从旧范围移出并移入您所在位置的新范围自动希望(使用 after_save
回调)。
在这方面,您应该能够做到这一点,然后在您的两个范围中的每一个中重新获取项目,并将它们 return 到您的前端以更新您的显示。
终于有了一些工作,这是我的工作代码:
def update_position
@categories = []
## if item moved to new scope ##
if params[:new_category_id]
@category_item.update(category_id: params[:new_category_id])
@category_item.insert_at(params[:new_index])
@categories << Category.find(params[:new_category_id])
@categories << Category.find(params[:category_id])
## else if item was moved within same scope ##
else
@category_item.insert_at(params[:new_index])
@categories << Category.find(params[:category_id])
end
@categories
render :index
end