Search code examples
activemodelruby-on-rails-6applicationcontroller

How to pass an empty text array from view to controller for update in Rails?


I have a Model that has a text array field defined like this

t.text "widgets", default: [], array: true and a permitted param in my controller

params.require(:model).permit(widgets: [])

In my view I can build values to put into this array using input fields that look like

<input multiple="true" value="widget_name" name="model[widgets][]" id="model_widgets"/>

This works great for all cases of adding and removing values from the array except for the case of removing the last value from the array if it already exists in the model

Is there a magical hidden input I can use to pass an empty array back to the controller for update?

I know checkboxes have a similar issue, in which you can use a hidden field with a value of 0. I've tried using similar methods like

<input type="hidden" value="0" name="model[widgets][]" id="model_widgets"/> which returns an array to the controller that looks like ["0"] (obviously),

<input type="hidden" value="0" name="model[widgets]" id="model_widgets"/> which is an unpermitted param in the controller (since it's not an array), and even

<input type="hidden" value="[]" name="model[widgets]" id="model_widgets"/> - also unpermitted, was hoping for an empty array.

I know I can accomplish this task by enabling a new parameter in my controller that when present indicates an empty widgets array but am hoping there's a "correct" approach from the view's perspective.


Solution

  • The answer seems to be no - there is no magical hidden input to pass an empty array back to the controller. Since a model won't update a field for an absent parameter, default values should be used in the controller.

    I found an elegant way of accomplishing this with permit params, I used the rails provided reverse_merge to set defaults for the arrays, so that if they're left out of the POST parameters they'll be empty rather than absent (i.e. removing all hidden inputs for that parameter will result in an empty array being used).

    def permitted_params
      defaults = { widgets: [] }
      params.require(:model).permit(widgets: []).reverse_merge(defaults)
    end