'Collection_select with multiple set as true. Why params[:book][:genre_ids] = ["","1","2"]

I using a has_many :through many-to-many relation in a multi-select via collection_select :multiple => true. I don't understand, why "genre_ids"=>["", "2", "3", "4"] always has empty first element?

Models:

class Book < ApplicationRecord
  has_many :book_genres
  has_many :genres, through: :book_genres
end

class Genre < ApplicationRecord
  has_many :book_genres
  has_many :books, through: :book_genres
end

class BookGenre < ApplicationRecord
  belongs_to :book
  belongs_to :genre
end

_form.html.erb

<%= form_for @book do |f| %>
  ...
  <%= f.collection_select(:genre_ids, Genre.all, :id, :genre, {include_blank: "Select genre"}, {multiple: true, size: 6}) %>
  ...
  <%= f.submit %>
<% end %>

books_controller.rb

class BooksController < ApplicationController
  def new
    @book = Book.new
  end
  def create
    render plain: params.inspect
  end
end

Parameters:

<ActionController::Parameters {
  "utf8"=>"✓",
  "authenticity_token"=>"8WRSXJHwMyHM....",
  "book"=>{"title"=>"",
           "genre_ids"=>["", "2", "3", "4"],
           "desc"=>"",
           "published_at"=>"1982"}, 
  "commit"=>"Create Book", 
  "controller"=>"books",
  "action"=>"create"} permitted: false>


Solution 1:[1]

May be it's because the "Select genre" is also selected since, it is a multiple select dropdown. Either remove it before sending the data over to the server or avoid using the "include_blank" option.

Solution 2:[2]

This is an intentional Rails behavior to work around a limitation of the HTML spec. HTML says that an unselected item should be sent to the server in the same way as the absence of that item would be. For example, if you select zero items, your browser will send a request with that attribute entirely missing from the request, as if the entire select element didn't exist.

But this puts us in a bad spot because normally, a parameter not specified in the request means that we shouldn't change the attribute. So we cannot tell the difference between "the user didn't want to change this value" and "the user wanted to clear this value". Therefore Rails adds a hidden form element for every select that adds a blank string, so the browser will always send the list.

See: https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pragash
Solution 2 Ben Carlsson