Rails - Strong parameters with empty arrays
This solution won't work for all cases:
params.require(:photo).permit(:tags, tags: [])
For example, if you are using MongoDB and you have a tag_ids array, which stores the ids in a has_many collection, your tag_ids attribute MUST be an array if you specify "type: Array" for the attribute in your model. Consequently, it won't work to send tag_ids with a nil value even if you do this:
params.require(:photo).permit(:tag_ids, tag_ids: [])
Mongoid, the official Ruby adapter for MongoDB, will complain the value of tag_ids must be an array.
The solution is you can indeed send an empty array via your form! And it doesn't need to be a json request. You can simply use remote: true on your form and send it via type: :js. How to do it? Simple. Just add a hidden input in your form and set its value to an empty string:
<%= form_for @user, remote: true, html: { class: 'form' } do |f| %>
<%= select_tag("#{f.object_name}[tag_ids][]", options_for_select(Tag.all.collect {|t| [t.name, c.id]}, selected: f.object.tag_ids), { class: 'form-control', multiple: 'multiple' }) %>
<%= hidden_field_tag "#{f.object_name}[tag_ids][]", '' %>
<%= f.submit class: 'btn ink-reaction btn-raised btn-primary' %>
<% end %>
This here is the key:
<%= hidden_field_tag "#{f.object_name}[tag_ids][]", '' %>
Your attribute will be stored as an empty array in your database. Note I only tested this with Mongoid, but I assume it carries the same functionality in ActiveRecord.
This is quite late, but I just had this problem myself. I solved it by including both the scalar version and array version in the permit statement, like so:
params.require(:photo).permit(:tags, tags: [])
FYI - it has to have both in the same permit statement - if you chain them it'll get thrown out for some reason.
EDIT: I just noticed that an empty array submitted via this method will be turned into nil - I've now got a bunch of fields that should be empty arrays that are nil. So the solution I posted doesn't actually work for me.
EDIT the second: Thought I had already added this, but this problem is related to Rails performing deep_munge on params hashes. This comment explains how to fix it: https://stackoverflow.com/a/25428800/130592
The temporary solution I've come down to is:
params[:foo_ids] ||= [] if params.has_key?(:foo_ids)
params.permit(foo_ids: [])
Here, foo_ids
is set to an empty array only if is passed. If it is not passed in the request, it is ignored.
I'm still hoping to find a better solution to this, as this sort of thing will be quite common in the project I'm working on - please do suggest better ideas if you have any.