Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
124
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

@roba4coding

formのinput valueを配列で受け取りたい場合のname属性

RailsのControllerで、以下のような複数itemで構成されるinputのvalueを配列で受け取りたい場合。

<div>
  <h2>Item1</h2>
  <input type="text" name="item[name_0]" value="yamada" />
</div>
<div>
  <h2>Item2</h2>
  <input type="text" name="item[name_1]" value="suzuki" />
</div>

このまま送信するとController#paramsの内容はこのようになる。

{
  "item" => {
    "name_0" => "yamada", 
    "name_1" => "suzuki"
  }
}

もちろんこれでは配列で取得できない。そこで以下のようにする。

<div>
  <h2>Item1</h2>
  <input type="text" name="item[][name]" value="yamada" />
</div>
<div>
  <h2>Item2</h2>
  <input type="text" name="item[][name]" value="suzuki" />
</div>

HTMLでitem[][name]とすると、params[:item]は配列になる

{
  "item" => [
    {"name" => "yamada"},
    {"name" => "suzuki"}
  ]
}

お、じゃあ、これでいいじゃん。と思ったら落とし穴が待っていた。checkboxである。

input type="checkbox"は、チェックした場合にしか送信されない。それは都合が悪い場合も多いので、input type="hidden"にチェックボックスオフ時の値をvalueに入れておくのが定石になっている。

<input type="hidden" name="item[delete_flag]" value="0" />
<input type="checkbox" name="item[delete_flag]" value="1" />

こうすると、チェックしない時はhiddenのvalueが送信され、チェックした時はcheckboxのvalueが送信される。Railsのform helperのcheck_boxメソッドも、hiddenとcheckboxを一緒に出力するようになっている。

<%= form_for User.new do |f| %>
  <% # input type="hidden"と"checkbox"を出力する %>
  <%= f.check_box :delete_flag %>
<% end %>

で、話を戻して配列化の話。checkboxもこのようにすると良いように思える。

<div>
  <h2>Item1</h2>
  <input type="text" name="item[][name]" value="yamada" />
  <input type="hidden" name="item[][delete_flag]" value="0" />
  <input type="checkbox" name="item[][delete_flag]" value="1" />
</div>
<div>
  <h2>Item2</h2>
  <input type="text" name="item[][name]" value="suzuki" />
  <input type="hidden" name="item[][delete_flag]" value="0" />
  <input type="checkbox" name="item[][delete_flag]" value="1" />
</div>

これでItem1のcheckbox(delete_flag)はチェックなし、Item2はチェックありで送信した時、Controller#paramsの内容はこうなるだろうと期待する。

{ 
  "item"=>[
    {"name"=>"yamada", "delete_flag"=>"0"}, 
    {"name"=>"suzuki", "delete_flag"=>"1"}
  ]
}

しかし、実際はこうなる・・・

{ 
  "item"=>[
    {"name"=>"yamada", "delete_flag"=>"0"}, 
    {"name"=>"suzuki", "delete_flag"=>"0"}, 
    {"delete_flag"=>"1"}
  ]
}

逆にItem1はチェックあり、Item2はチェックなしにするとこうなる。

{
  "item"=>[
    {"name"=>"yamada", "delete_flag"=>"0"}, 
    {"delete_flag"=>"1", "name"=>"suzuki"}, 
    {"delete_flag"=>"0"}
  ]
}

だめだめである・・・

いくつか方法はあると思うが、私はこのようにすることにした。

<input type="text" name="item[0][name]" value="yamada" />
<input type="hidden" name="item[0][delete_flag]" value="0" />
<input type="checkbox" name="item[0][delete_flag]" value="1" />

<input type="text" name="item[1][name]" value="suzuki" />
<input type="hidden" name="item[1][delete_flag]" value="0" />
<input type="checkbox" name="item[1][delete_flag]" value="1" />

item[]ではなくitem[0]、item[1]のようにindex値を入れるようにした。そしてItem1はチェックなし、Item2はチェックありで送信すると内容はこうなる。

{
  "item"=>
    {
      "0"=>{"name"=>"yamada", "delete_flag"=>"0"}, 
      "1"=>{"name"=>"suzuki", "delete_flag"=>"1"}
    }
}

Item1チェックあり、Item2はチェックなしの場合はこうなる。

{
  "item"=>
    {
      "0"=>{"name"=>"yamada", "delete_flag"=>"1"}, 
      "1"=>{"name"=>"suzuki", "delete_flag"=>"0"}
    }
}

itemは配列ではなくHashになるが、以下のように簡単に配列化できる。

items = params[:item].keys.sort.map { |index| params[:item][index] }

itemsの内容は以下のようになる。

[
  {"name"=>"yamada", "delete_flag"=>"0"}, 
  {"name"=>"suzuki", "delete_flag"=>"1"}
]

form helperを使って出力する

これまでは理解しやすいように直接HTMLタグを直接書いてきたが、実際はform helperを使うことになるだろう。その場合は、以下のようにname属性を明示的に指定すると良い。

<%= form_for @group, url: url_for(action: :debug) do |f| %>
  <% @group.items.each_with_index do |item, index| %>
    <%= f.fields_for item do |f| %>
      <%= f.text_field :name, name: "item[#{index}][name]" %>
      <%= f.check_box :delete_flag, name: "item[#{index}][delete_flag]" %>
    <% end %>
  <% end %>
  <%= f.submit %>
<% end %>

@groupは、以下のようなItemGroupモデルのオブジェクトとする。

class ItemGroup
  # Itemとのリレーション
  has_many :items
end

class Item
  # itemsテーブルは、name, delete_flagをカラムに持つ
end

ActiveAdminのsemantic_form_forの場合

以下のように書くことができる。

<%= semantic_form_for [:admin, @group], 
                        builder: ActiveAdmin::FormBuilder do |f| %>
  <% @group.items.each_with_index do |item, index| %>
    <%= f.fields_for :items, items do |f| %>
      <%= f.input :name, 
                  input_html: { name: "item[#{index}][name]" } %>
      <%= f.input :delete_flag, 
                  input_html: { name: "item[#{index}][delete_flag]" } %>
    <% end %>
  <% end %>
<% end %>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
124
Help us understand the problem. What are the problem?