Search code examples
ruby-on-railsformsruby-on-rails-4put

Rails multiple select puts extra blank parameter


I'm trying to create an HTML multiple select using collection_select, to be able to update an entity (a Student) which has a collection of another entity (a SubscriptionList) as a nested attribute. This is backed up by a HABTM ActiveRecord's relation.
I have created the following form for the Student via scaffolding:

<div class="field">
  <%= f.label :first_name %><br>
  <%= f.text_field :first_name %>
</div>
<div class="field">
  <%= f.label :last_name %><br>
  <%= f.text_field :last_name %>
</div>
<div class="field">
  <%= f.label :file_number %><br>
  <%= f.text_field :file_number %>
</div>
<div class="field">
  <%= f.label :subscription_list %><br>
  <%= f.collection_select(:subscription_lists, SubscriptionList.all, :id, :name, {}, {:multiple => true}) %>
</div>

<div class="actions">
  <%= f.submit %>
</div>

and it draws the multiple select properly.
However, if I fill the form and try to PUT the entity, I'm getting this as the params:

{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"vXYMRYI1UtX7WJRZM0OPIhHQSSEyNOPyUxkUvScdu45PTL7qVhvlJfQYNvaKG5rw+mvHAAAbf6ViTQ6tE4lV1Q==", "student"=>{"first_name"=>" Mariana", "last_name"=>"González", "file_number"=>"12345678", "subscription_lists"=>["", "3"]}, "commit"=>"Update Student", "controller"=>"students", "action"=>"update", "id"=>"14"}

So, my Student is

"first_name"=>" Mariana", "last_name"=>"González", "file_number"=>"12345678", "subscription_lists"=>["", "3"]}

I find it very strange to be receiving ["", "3"] as the values. Why I'm receiving this first "" value?

I'm also posting here my Controller (actions other that update have been deleted for brevity)

class StudentsController < ApplicationController
  before_action :set_student, only: [:show, :edit, :update, :destroy, :enrollments]

  # PATCH/PUT /students/1
  # PATCH/PUT /students/1.json
  def update
    puts "\n\n\n\n\n\n\n\n\nThese are the params: #{params.inspect}"
    puts "\n\n\n\n\nThis is student_params object: #{student_params.inspect}\n\n\nand its class #{student_params.class}"
    #puts "\n\n\n\n\n\n\n\n\nWill look for SL with ID: #{params[:subscription_lists_id]}"
    all_ids = student_params.subscription_lists.collect {|sl| sl.id }
    @student.subscription_lists = SubscriptionList.find(all_ids)
    #@student.subscription_lists = SubscriptionList.where(id: all_ids)
    respond_to do |format|
      if @student.update(student_params)
        format.html { redirect_to @student, notice: 'Student was successfully updated.' }
        format.json { render :show, status: :ok, location: @student }
      else
        format.html { render :edit }
        format.json { render json: @student.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_student
      @student = Student.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def student_params
      #params[:student]
      #params.require(:foo).permit(:bar, {:baz => [:x, :y]})
      #params.require(:student).permit(:first_name, :last_name, :file_number, :subscription_lists)

      params.require(:student).permit!    # No strong parameters...
    end
end

In fact, I'd rather receive a Student with a nested collection of SubscriptionList instead of just receving an array of ids, but I'm not sure if this is even possible.

Any help would be really appreciated.
Best regards


Solution

  • About your question has been answered in collection select always adding blank value.

    You will get [""] when you did not select anything or you select your options select as default. To avoid this you have to add hidden_field before the collection select.

    <div class="field">
      <%= f.label :subscription_lists %><br>
      <%= f.hidden_field :subscription_lists %>
      <%= f.collection_select(:subscription_lists, SubscriptionList.all, :id, :name, {}, {:multiple => true}) %>
    </div>
    

    The hidden_field helps you when it's nothing selected. How about when you choose it? Please try this.

      def update
        if student_params["subscription_lists"].any?
          student_params["subscription_lists"].reject!(&:empty?)
        end
        respond_to do |format|
          if @student.update(student_params)
            format.html { redirect_to @student, notice: 'Student was successfully updated.' }
            format.json { render :show, status: :ok, location: @student }
          else
            format.html { render :edit }
            format.json { render json: @student.errors, status: :unprocessable_entity }
          end
        end
      end
    

    I hope this help you.