80

I have a model that has an attribute that is an Array. What's the proper way for me to populate that attribute from a form submission?

I know having a form input with a field whose name includes brackets creates a hash from the input. Should I just be taking that and stepping through it in the controller to massage it into an array?

Example to make it less abstract:

class Article
  serialize :links, Array
end

The links variable takes the form of a an array of URLs, i.e. [["http://www.google.com"], ["http://stackoverflow.com"]]

When I use something like the following in my form, it creates a hash:

<%= hidden_field_tag "article[links][#{url}]", :track, :value => nil %>

The resultant hash looks like this:

"links" => {"http://www.google.com" => "", "http://stackoverflow.com" => ""}

If I don't include the url in the name of the link, additional values clobber each other:

<%= hidden_field_tag "article[links]", :track, :value => url %>

The result looks like this: "links" => "http://stackoverflow.com"

engineersmnky
  • 25,495
  • 2
  • 36
  • 52
William Jones
  • 18,089
  • 17
  • 63
  • 98

8 Answers8

102

If your html form has input fields with empty square brackets, then they will be turned into an array inside params in the controller.

# Eg multiple input fields all with the same name:
<input type="textbox" name="course[track_codes][]" ...>

# will become the Array 
   params["course"]["track_codes"]
# with an element for each of the input fields with the same name

Added:

Note that the rails helpers are not setup to do the array trick auto-magically. So you may have to create the name attributes manually. Also, checkboxes have their own issues if using the rails helpers since the checkbox helpers create additional hidden fields to handle the unchecked case.

Larry K
  • 47,808
  • 15
  • 87
  • 140
  • 1
    If you care about the order of array elements being preserved and want to be _really_ picky about standards, it might be worth reading [this question](http://stackoverflow.com/questions/4400698/is-the-order-of-inputs-in-a-post-guaranteed-for-array-inputs-in-php). It sounds like the HTML specs imply, but are not 100% clear, that order should be preserved, but all browsers seem to do it. – antinome Mar 20 '13 at 16:21
  • 1
    html like `` will generate params `{course: {track_codes: [{field1: "yourvalue", field2: "yourvalue"},{field1: "yourvalue", field2: "yourvalue"}]}}` – Fabrizio Bertoglio Jan 18 '19 at 14:34
  • @FabrizioBertoglio what if we want to send multiple tags is a single input field? – Humayun Naseer Aug 24 '21 at 08:44
63
= simple_form_for @article do |f|
  = f.input_field :name, multiple: true
  = f.input_field :name, multiple: true
  = f.submit
stAndrei
  • 667
  • 5
  • 5
  • This is what I was looking for, but was unable to make it work properly. I have asked for help [here](http://stackoverflow.com/questions/27412086/using-arrays-in-simple-form/27432898#27432898), but note that there is an [open issue](https://github.com/plataformatec/simple_form/issues/1124) asking for simple_form to support arrays. – Obromios Dec 11 '14 at 21:43
  • 17
    Please add an explanation what this code does. Like this the answer is not very helpful. – idmean Sep 08 '15 at 09:56
  • 1
    The accepted answer should be updated to include this `multiple: true` update, which is clearly the way to go. – DannyB Jan 01 '16 at 14:08
  • 4
    `f.input :name, input_html: { multiple: true }` could be used for input – Canbey Bilgili Jan 01 '18 at 15:00
  • Note: this answer requires the `simple_form` gem: https://github.com/plataformatec/simple_form – Jason Axelson Jan 30 '19 at 02:46
  • 2
    `f.input: name, multiple: true, value: ...` also works with Rails native `form_for @article do |f| ` – Alexis Delahaye Jun 06 '19 at 09:49
54

TL;DR version of HTML [] convention:

Array:

<input type="textbox" name="course[track_codes][]", value="a">
<input type="textbox" name="course[track_codes][]", value="b">
<input type="textbox" name="course[track_codes][]", value="c">

Params received:

{ course: { track_codes: ['a', 'b', 'c'] } }

Hash

<input type="textbox" name="course[track_codes][x]", value="a">
<input type="textbox" name="course[track_codes][y]", value="b">
<input type="textbox" name="course[track_codes][z]", value="c">

Params received:

{ course: { track_codes: { x: 'a', y: 'b', z: 'c' } }
ecoologic
  • 10,202
  • 3
  • 62
  • 66
9

I've also found out that if pass your input helper like this you will get an array of courses each one with its own attributes.

# Eg multiple input fields all with the same name:
<input type="textbox" name="course[][track_codes]" ...>

# will become the Array 
   params["course"]

# where you can get the values of all your attributes like this:
   params["course"].each do |course|
       course["track_codes"]
   end    
Ana Franco
  • 1,611
  • 3
  • 24
  • 43
8

I just set up a solution using jquery taginput:

http://xoxco.com/projects/code/tagsinput/

I wrote a custom simple_form extension

# for use with: http://xoxco.com/projects/code/tagsinput/

class TagInput < SimpleForm::Inputs::Base

  def input
    @builder.text_field(attribute_name, input_html_options.merge(value: object.value.join(',')))
  end

end

A coffeescrpt snippet:

$('input.tag').tagsInput()

And a tweak to my controller, which sadly has to be slightly specific:

@user = User.find(params[:id])
attrs = params[:user]

if @user.some_field.is_a? Array
  attrs[:some_field] = attrs[:some_field].split(',')
end
Peter Ehrlich
  • 6,969
  • 4
  • 49
  • 65
2

I had a similar issue, but wanted to let the user input a series of comma separated elements as the value for the array. My migration uses rails new ability (or is it postrges' new ability?) to have an array as the column type

add_column :articles, :links, :string, array: true, default: []

the form can then take this input

<%= text_field_tag "article[links][]", @article.links %>

and it means the controller can operate pretty smoothly as follows

def create
  split_links
  Article.create(article_params)
end

private
def split_links
  params[:article][:links] = params[:article][:links].first.split(",").map(&:strip)
end


params.require(:article).permit(links: [])

Now the user can input as many links as they like, and the form behaves properly on both create and update. And I can still use the strong params.

ian root
  • 349
  • 1
  • 11
0

For those who use simple form, you may consider this solution. Basically need to set up your own input and use it as :array. Then you would need to handle input in your controller level.

#inside lib/utitilies
class ArrayInput < SimpleForm::Inputs::Base
  def input
    @builder.text_field(attribute_name, input_html_options.merge!({value: object.premium_keyword.join(',')}))
  end
end

#inside view/_form

...
= f.input :premium_keyword, as: :array, label: 'Premium Keyword (case insensitive, comma seperated)'

#inside controller
def update
  pkw = params[:restaurant][:premium_keyword]
  if pkw.present?
    pkw = pkw.split(", ")
    params[:restaurant][:premium_keyword] = pkw
  end

  if @restaurant.update_attributes(params[:restaurant])
    redirect_to admin_city_restaurants_path, flash: { success: "You have successfully edited a restaurant"}
  else
    render :edit
  end   
end

In your case just change :premium_keyword to the your array field

sovanlandy
  • 1,700
  • 2
  • 19
  • 24
0

I had some trouble editing the array after implementing this for my new.html.erb, so I'll drop my solution to that problem here:

Edit a model property of type array with Rails form?

Jack Collins
  • 339
  • 1
  • 2
  • 13