【问题标题】:How to handle drag and drop between multiple lists using SortablejS and acts_as_list gem?如何使用 SortablejS 和acts_as_list gem 处理多个列表之间的拖放?
【发布时间】:2020-03-31 04:29:45
【问题描述】:

我一直在寻找一天半的解决方案。

我有几个表(类别),允许用户拖放每一行(项目)以重新排序每个项目在该表(类别)上的位置。我正在使用SortableJS 来处理拖放,并且我拥有它以便在同一类别中重新排序。

在我的后端,我有一个 Rails API,它使用 Jbuilder 返回 JSON,并使用 acts_as_list gem 为我处理位置。

当我将一个项目从类别 A 拖到类别 B 时,我在弄清楚如何处理重新排序时遇到问题。我认为问题在于控制器操作,并且我无法想出一个解决方案来接收多个更新类别,然后使用 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

【问题讨论】:

    标签: ruby-on-rails jbuilder sortablejs acts-as-list


    【解决方案1】:

    acts_as_list 允许您设置列表项的新范围参数和位置,然后只需保存该项目,该项目将自动移出旧范围并进入您想要的位置的新范围(使用after_save 回调)。

    在这方面,您应该能够做到这一点,然后重新获取两个范围中的项目并将它们返回到您的前端以更新您的显示。

    【讨论】:

    • 我想我昨晚很晚才知道的!我在这里发布了一个答案,介意检查一下,看看这是否是一个好的工作解决方案?顺便说一句,我喜欢你在这里为自己的宝石回答问题如此积极。让像我这样简单的人的生活更轻松!
    • 不客气 :) 我不得不承认,我只是 gem 的维护者。其他人完成了 gem 的大部分创作 :)
    【解决方案2】:

    终于搞定了,这是我的工作代码:

        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
    

    【讨论】:

    • 这基本上是对的,但是您可以在更新子句中包含位置:@category_item.update(category_id: params[:new_category_id], position: params[:new_index])。 act_as_list 旨在让您设置位置和范围,一切都会被处理。至于在前端同步列表,您可以只依靠更新成功,而不是将任何东西推到前面,除了 head :no_content。取决于您的界面以及您是否担心并发更新。
    猜你喜欢
    • 2020-06-04
    • 1970-01-01
    • 2017-12-20
    • 1970-01-01
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多