Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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 %>
roba4coding
プログラマ&ディベロッパー&ハッカー。エンジニアではありません。主にアプリの開発をやってます。勉強会には一切顔を出さず、たいていウェブで済ませて独学。家に技術本らしきものは一冊もありません。炎上案件に召喚され黒魔法で消火活動を行う暗黒プログラマです。記事の内容による損害について当方は一切責任を負いませんのでご了承ください。
https://note.mu/roba4coding
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした