LoginSignup
137

More than 5 years have passed since last update.

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

Posted at

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 %>

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
137