Search code examples
ruby-on-railsjquery-ui-sortablenested-attributes

Ruby on Rails - Want to sort the nested attributes when sorting through jquery-ui-sortable


In my rails app I have nested attributes. I'm using jquery-ui-sortable to sort the nested field sets.

On form submit I want to maintain the sorted order and to display the field sets in this when opening in edit mode. I want to save the sorted position of each nested field set in database.

I tried the acts_as_list gem, but was unable to get it working. I'm currently using the following logic and it works fine.

I want to know whether this logic is correct or not. Kindly give your suggestions on it. Thanks for your responses in advance.

The logic is:

  1. I have a table column position for each field set and have default_scope { order("position ASC") } in my model
  2. When the form is submitted, the controller gets the raw params. Before updating, I set the position for each field and use this for further processing

I get the following raw params:

{
  "utf8" => "✓", "_method" => "put", "job" => {
    "name" => "my another test",
    "fields_attributes" => {
      "1" => {
        "field_value" => "Id",
        "field_id" => "1",
        "_destroy" => "false",
        "id" => "9"
      },
      "0" => {
        "field_value" => "Name",
        "field_id" => "2",
        "_destroy" => "false",
        "id" => "10"
      }
    },
    "include_header" => "1"
  }, "search" => "", "controller" => "jobs", "action" => "update", "id" => "4"
}
permitted: false

and my controller code is:

before_action :set_position, only: :update

def update
  @job = Job.where(id: params[:id]).first

  if @job.update_attributes(job_params)
    redirect_to jobs_url
  end
end

def set_position
  index = 1
  params[:job][:fields_attributes].each do |f, params|
    params[:position] = index
    index += 1
  end
end

def data_job_params
  params.require(:job).permit(:name, :user_id, :include_header, fields_attributes: %i(id field_value field_id position _destroy))
end

It's working fine, but I want to know whether I am doing this in the correct way or not.


Solution

  • There are a few things that might be handy here:

    Firstly, you could use each_with_index to iterate through params[:job][:fields_attributes] - something like:

    def set_position
      params[:job][:fields_attributes].each_with_index do |(f, fieldset_params), i|
        fieldset_params[:position] = index + 1 # << index starts at 0
      end
    end
    

    You'll also notice that the key to each object in params[:job][:fields_attributes] is actually the number of the object in the hash, so, if you're happy with it, you could use that:

    def set_position
      params[:job][:fields_attributes].each do |f, fieldset_params|
        fieldset_params[:position] = f
      end
    end
    

    You'll notice I've changed params to fieldset_params in both of these examples - it saves a little confusion / potential errors by not shadowing the outer variable.

    You could also have a hidden field in your view code for position, starting from 1. If you edit your question to include the view code, I can show you how you'd do that.

    Hope something in there can help - let me know how you get on!