首页 > 解决方案 > 如何使用 Sortable.js、acts_as_list 和 Stimulus 为nested_attributes 获得正确的位置

问题描述

我正在尝试使用nested_attributes实现“拖放排序列表”(https://gorails.com/episodes/rails-drag-and-drop-sortable )。

不幸的是,我弄错了:position。即使总共应该只有三个位置,它也会从 2 跳到 5 到 7。

我认为问题在于隐藏的输入标签也被计算在内。如果这是主要问题,有什么办法解决吗?

// config/routes.rb

  resources :recipes, except: :index do
    resources :steps do
      resource :step_position, only: :update
    end
  end

// app/views/recipes/_form.html.erb

<div data-controller="sortable" data-sortable-url="/recipes/:recipe_id/steps/:id/step_position">
  <%= f.fields_for :steps, @recipe.steps.order(position: :asc) do |step| %>
    <div data-id="<%= step.object.id %>">
      <%= step.text_area :description %>
    </div>
  <% end %>
</div>
// app/javascript/controllers/sortable_controller.js

import { Controller } from "stimulus";
import Rails from "@rails/ujs";
import { Sortable } from "sortablejs";

export default class extends Controller {
  connect() {
    this.sortable = Sortable.create(this.element, {
      onEnd: this.end.bind(this),
    });
  }

  end(event) {
    let id = event.item.dataset.id;
    let data = new FormData();
    data.append("position", event.newIndex + 1);
    Rails.ajax({
      url: this.data.get("url").replace(":id", id),
      type: "PATCH",
      data: data,
    });
  }
}
// app/controllers/step_positions_controller.rb

class StepPositionsController < ApplicationController
  def update
    @step = Step.find(params[:step_id])
    authorize @step.recipe

    @step.insert_at(params[:position].to_i)

    head :ok
  end
end

标签: ruby-on-railsnested-attributessortablejsacts-as-liststimulusjs

解决方案


首先看一下传递给服务器的参数。这些看起来好吗?如果没有,那么 javascript 可能是罪魁祸首。

其次,insert_at在这种情况下,它不是工作的最佳工具。如果您想显式设置位置而不受回调等干扰...我通常where(:id => id).update_all(:position => the_position)在列表中的每个元素上使用(前提是您知道您的参数中包含所有元素。

最好的解决方案是让 javascript 向您发送要移动的项目以及您希望它在列表中的位置,然后只需将位置列设置为该值,acts_as_list并将为您洗牌。唯一的缺点是来自其他用户的并发更新。如果是这种情况,您的列表将不同步。


推荐阅读