edge
詳細はrubyonrails.orgで: もっとRuby on Rails

Action Viewフォームヘルパー

Webアプリケーションのフォームは、ユーザーの入力のための重要なインターフェースです。ただし、フォームのマークアップは、フォームコントロールの名前付けやその多くの属性の処理が必要なため、すぐに書くのが面倒になり、メンテナンスが難しくなることがあります。Railsは、フォームマークアップを生成するためのビューヘルパーを提供することで、この複雑さを取り除きます。ただし、これらのヘルパーメソッドには異なる使用例があるため、開発者はヘルパーメソッドの違いを知っておく必要があります。

このガイドを読むことで、以下のことがわかります。

注意: このガイドは、利用可能なフォームヘルパーとその引数の完全なドキュメントではありません。すべての利用可能なヘルパーの完全なリファレンスについては、Rails APIドキュメントを参照してください。

1 基本的なフォームの処理

メインのフォームヘルパーはform_withです。

<%= form_with do |form| %>
  フォームの内容
<% end %>

このように引数なしで呼び出されると、現在のページにPOSTされるフォームタグが作成されます。たとえば、現在のページがホームページであると仮定すると、生成されるHTMLは次のようになります。

<form accept-charset="UTF-8" action="/" method="post">
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  フォームの内容
</form>

HTMLには、hiddenタイプのinput要素が含まれていることに注意してください。このinputは重要です。なぜなら、GET以外のフォームは、これなしでは正常に送信できないからです。 名前がauthenticity_tokenの非表示の入力要素は、Railsのセキュリティ機能であるクロスサイトリクエストフォージェリ保護です。フォームヘルパーは、このセキュリティ機能が有効な場合に、すべてのGET以外のフォームに対してこれを生成します。詳細については、Railsアプリケーションのセキュリティガイドを参照してください。

1.1 一般的な検索フォーム

Web上で最も基本的なフォームの1つは、検索フォームです。このフォームには以下が含まれます。

  • "GET"メソッドを持つフォーム要素
  • 入力のためのラベル
  • テキスト入力要素
  • 送信要素

このフォームを作成するには、form_withとそれが生成するフォームビルダーオブジェクトを使用します。次のようになります。

<%= form_with url: "/search", method: :get do |form| %>
  <%= form.label :query, "Search for:" %>
  <%= form.text_field :query %>
  <%= form.submit "Search" %>
<% end %>

これにより、次のHTMLが生成されます。

<form action="/search" method="get" accept-charset="UTF-8" >
  <label for="query">Search for:</label>
  <input id="query" name="query" type="text" />
  <input name="commit" type="submit" value="Search" data-disable-with="Search" />
</form>

form_withurl: my_specified_pathを渡すことで、リクエストを行う場所をフォームに指定できます。ただし、以下で説明するように、フォームにActive Recordオブジェクトを渡すこともできます。

各フォーム入力には、その名前からID属性が生成されます(上記の例では"query")。これらのIDは、CSSスタイリングやJavaScriptでのフォームコントロールの操作に非常に便利です。

重要: 検索フォームではメソッドとして"GET"を使用してください。これにより、ユーザーは特定の検索をブックマークし、それに戻ることができます。一般的に、Railsはアクションに適切なHTTP動詞を使用することを推奨しています。

1.2 フォーム要素を生成するためのヘルパー

form_withによって生成されるフォームビルダーオブジェクトは、テキストフィールド、チェックボックス、ラジオボタンなどのフォーム要素を生成するための多くのヘルパーメソッドを提供します。これらのメソッドへの最初のパラメータは常に入力の名前です。 フォームが送信されると、名前はフォームデータとともに渡され、ユーザーが入力した値がコントローラのparamsに渡されます。たとえば、フォームに<%= form.text_field :query %>が含まれている場合、コントローラでこのフィールドの値をparams[:query]で取得できます。 Railsは、入力の名前付けに特定の規則を使用しています。これにより、配列やハッシュなどのスカラー以外の値を含むパラメータを送信することが可能になり、これらの値はparamsでアクセスできます。詳細については、このガイドのパラメータの命名規則の理解セクションで詳しく説明しています。これらのヘルパーの正確な使用方法については、APIドキュメントを参照してください。

1.2.1 チェックボックス

チェックボックスは、ユーザーが有効または無効にできるオプションのセットを提供するフォームコントロールです。

<%= form.check_box :pet_dog %>
<%= form.label :pet_dog, "I own a dog" %>
<%= form.check_box :pet_cat %>
<%= form.label :pet_cat, "I own a cat" %>

これにより、以下のような出力が生成されます。

<input type="checkbox" id="pet_dog" name="pet_dog" value="1" />
<label for="pet_dog">I own a dog</label>
<input type="checkbox" id="pet_cat" name="pet_cat" value="1" />
<label for="pet_cat">I own a cat</label>

check_boxの最初のパラメータは、入力の名前です。チェックボックスの値(paramsに表示される値)は、オプションで3番目と4番目のパラメータを使用して指定することもできます。詳細については、APIドキュメントを参照してください。

1.2.2 ラジオボタン

ラジオボタンは、チェックボックスに似ていますが、ユーザーが1つだけ選択できるオプションのセットを指定するコントロールです。

<%= form.radio_button :age, "child" %>
<%= form.label :age_child, "I am younger than 21" %>
<%= form.radio_button :age, "adult" %>
<%= form.label :age_adult, "I am over 21" %>

出力:

<input type="radio" id="age_child" name="age" value="child" />
<label for="age_child">I am younger than 21</label>
<input type="radio" id="age_adult" name="age" value="adult" />
<label for="age_adult">I am over 21</label>

radio_buttonの2番目のパラメータは、入力の値です。これらの2つのラジオボタンは同じ名前(age)を共有しているため、ユーザーはそれらのうちの1つしか選択できず、params[:age]には"child"または"adult"が含まれます。

注意: チェックボックスとラジオボタンには常にラベルを使用してください。ラベルは特定のオプションにテキストを関連付け、クリック可能な領域を拡大することで、ユーザーが入力をクリックしやすくします。

1.3 その他の興味深いヘルパー

他の価値のあるフォームコントロールには、テキストエリア、非表示フィールド、パスワードフィールド、数値フィールド、日付と時刻フィールドなどがあります。

<%= form.text_area :message, size: "70x5" %>
<%= form.hidden_field :parent_id, value: "foo" %>
<%= form.password_field :password %>
<%= form.number_field :price, in: 1.0..20.0, step: 0.5 %>
<%= form.range_field :discount, in: 1..100 %>
<%= form.date_field :born_on %>
<%= form.time_field :started_at %>
<%= form.datetime_local_field :graduation_day %>
<%= form.month_field :birthday_month %>
<%= form.week_field :birthday_week %>
<%= form.search_field :name %>
<%= form.email_field :address %>
<%= form.telephone_field :phone %>
<%= form.url_field :homepage %>
<%= form.color_field :favorite_color %>

出力:

<textarea name="message" id="message" cols="70" rows="5"></textarea>
<input type="hidden" name="parent_id" id="parent_id" value="foo" />
<input type="password" name="password" id="password" />
<input type="number" name="price" id="price" step="0.5" min="1.0" max="20.0" />
<input type="range" name="discount" id="discount" min="1" max="100" />
<input type="date" name="born_on" id="born_on" />
<input type="time" name="started_at" id="started_at" />
<input type="datetime-local" name="graduation_day" id="graduation_day" />
<input type="month" name="birthday_month" id="birthday_month" />
<input type="week" name="birthday_week" id="birthday_week" />
<input type="search" name="name" id="name" />
<input type="email" name="address" id="address" />
<input type="tel" name="phone" id="phone" />
<input type="url" name="homepage" id="homepage" />
<input type="color" name="favorite_color" id="favorite_color" value="#000000" />

非表示の入力はユーザーに表示されず、テキスト入力と同様にデータを保持します。それらの内部の値はJavaScriptで変更することができます。

重要: 検索、電話、日付、時刻、色、日時、日時-ローカル、月、週、URL、メール、数値、範囲の入力は、HTML5のコントロールです。古いブラウザでも一貫した体験を提供する必要がある場合は、HTML5のポリフィル(CSSおよび/またはJavaScriptによって提供される)が必要です。多くの解決策が存在しますが、現在人気のあるツールはModernizrです。これは、検出されたHTML5の機能の存在に基づいて機能を追加する簡単な方法を提供します。 ヒント:パスワード入力フィールド(どの目的でも)を使用している場合は、アプリケーションを設定してこれらのパラメータがログに記録されないようにすることをお勧めします。これについては、Securing Rails Applicationsガイドで学ぶことができます。

2 モデルオブジェクトの取り扱い

2.1 フォームをオブジェクトにバインドする

form_with:model引数を使用すると、フォームビルダーオブジェクトをモデルオブジェクトにバインドできます。これにより、フォームはそのモデルオブジェクトにスコープされ、フォームのフィールドはそのモデルオブジェクトの値で自動的に埋められます。

たとえば、次のような@articleモデルオブジェクトがある場合:

@article = Article.find(42)
# => #<Article id: 42, title: "My Title", body: "My Body">

次のフォーム:

<%= form_with model: @article do |form| %>
  <%= form.text_field :title %>
  <%= form.text_area :body, size: "60x10" %>
  <%= form.submit %>
<% end %>

出力結果は次のようになります:

<form action="/articles/42" method="post" accept-charset="UTF-8" >
  <input name="authenticity_token" type="hidden" value="..." />
  <input type="text" name="article[title]" id="article_title" value="My Title" />
  <textarea name="article[body]" id="article_body" cols="60" rows="10">
    My Body
  </textarea>
  <input type="submit" name="commit" value="Update Article" data-disable-with="Update Article">
</form>

ここで注目すべき点がいくつかあります:

  • フォームのactionは、@articleに適切な値が自動的に埋められます。
  • フォームのフィールドは、@articleから対応する値で自動的に埋められます。
  • フォームのフィールド名はarticle[...]でスコープされます。これにより、params[:article]はこれらのフィールドの値を含むハッシュになります。パラメータの命名規則の詳細については、このガイドのUnderstanding Parameter Naming Conventions章を参照してください。
  • 送信ボタンには適切なテキスト値が自動的に設定されます。

ヒント:通常、入力フィールドはモデルの属性と同じになります。ただし、必ずしもそうである必要はありません!必要な他の情報がある場合は、属性と同様にフォームに含め、params[:article][:my_nifty_non_attribute_input]を介してアクセスできます。

2.1.1 fields_forヘルパー

fields_forヘルパーは、<form>タグをレンダリングせずに同様のバインディングを作成します。これを使用して、同じフォーム内で追加のモデルオブジェクトのフィールドをレンダリングすることができます。たとえば、Personモデルに関連するContactDetailモデルがある場合、次のようにして両方のための単一のフォームを作成できます:

<%= form_with model: @person do |person_form| %>
  <%= person_form.text_field :name %>
  <%= fields_for :contact_detail, @person.contact_detail do |contact_detail_form| %>
    <%= contact_detail_form.text_field :phone_number %>
  <% end %>
<% end %>

これにより、次の出力が生成されます:

<form action="/people" accept-charset="UTF-8" method="post">
  <input type="hidden" name="authenticity_token" value="bL13x72pldyDD8bgtkjKQakJCpd4A8JdXGbfksxBDHdf1uC0kCMqe2tvVdUYfidJt0fj3ihC4NxiVHv8GVYxJA==" />
  <input type="text" name="person[name]" id="person_name" />
  <input type="text" name="contact_detail[phone_number]" id="contact_detail_phone_number" />
</form>

fields_forによって生成されるオブジェクトは、form_withによって生成されるフォームビルダーと同様のものです。

2.2 レコードの識別に依存する

Articleモデルはアプリケーションのユーザーに直接利用されるため、Railsでの開発のベストプラクティスに従って、リソースとして宣言する必要があります:

resources :articles

ヒント:リソースを宣言すると、いくつかの副作用が発生します。リソースの設定と使用方法については、Rails Routing from the Outside Inガイドを参照してください。

RESTfulリソースを扱う場合、form_withへの呼び出しは、レコードの識別に依存すると、かなり簡単になります。要するに、モデルのインスタンスを渡すだけで、Railsがモデル名とその他の情報を自動的に解決してくれます。次の例の長いスタイルと短いスタイルは、同じ結果をもたらします:

## 新しい記事を作成する
# 長いスタイル:
form_with(model: @article, url: articles_path)
# 短いスタイル:
form_with(model: @article)

## 既存の記事を編集する
# 長いスタイル:
form_with(model: @article, url: article_path(@article), method: "patch")
# 短いスタイル:
form_with(model: @article)

短いスタイルのform_with呼び出しは、レコードが新しいか既存のかに関係なく、便利に同じです。レコードの識別は、record.persisted?を使ってレコードが新しいかどうかを判断します。また、適切なパスを選択し、オブジェクトのクラスに基づいて名前を選択します。 もしsingular resourceを持っている場合、form_withで動作させるためにresourceresolveを呼び出す必要があります。

resource :geocoder
resolve('Geocoder') { [:geocoder] }

警告: モデルにSTI(単一テーブル継承)を使用している場合、親クラスのみがリソースとして宣言されている場合、サブクラスのレコード識別には依存できません。明示的に:url:scope(モデル名)を指定する必要があります。

2.2.1 名前空間の扱い

名前空間のルートを作成した場合、form_withには便利なショートカットがあります。アプリケーションにadmin名前空間がある場合、

form_with model: [:admin, @article]

は、admin名前空間内のArticlesControllerにフォームを作成します(更新の場合はadmin_article_path(@article)に送信します)。名前空間のレベルが複数ある場合、構文は似ています。

form_with model: [:admin, :management, @article]

Railsのルーティングシステムと関連する規則についての詳細は、Rails Routing from the Outside Inガイドを参照してください。

2.3 PATCH、PUT、またはDELETEメソッドを使用したフォームはどのように動作しますか?

Railsフレームワークは、アプリケーションのRESTfulな設計を推奨しているため、フォームの送信には「PATCH」、「PUT」、「DELETE」のリクエストを頻繁に行うことになります(「GET」と「POST」以外のメソッドも含まれます)。ただし、ほとんどのブラウザは、フォームの送信に関して「GET」と「POST」以外のメソッドをサポートしていません。

Railsは、"_method"という名前の非表示の入力を使用して、他のメソッドをPOSTでエミュレートすることで、この問題を解決します。この入力は、指定されたメソッドを反映するように設定されます。

form_with(url: search_path, method: "patch")

出力:

<form accept-charset="UTF-8" action="/search" method="post">
  <input name="_method" type="hidden" value="patch" />
  <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
  <!-- ... -->
</form>

POSTされたデータを解析する際、Railsは特別な_methodパラメータを考慮に入れ、それに内部で指定されたHTTPメソッド(この例では「PATCH」)として動作します。

フォームをレンダリングする際、送信ボタンはformmethod:キーワードを使用して宣言されたmethod属性を上書きすることができます。

<%= form_with url: "/posts/1", method: :patch do |form| %>
  <%= form.button "Delete", formmethod: :delete, data: { confirm: "Are you sure?" } %>
  <%= form.button "Update" %>
<% end %>

<form>要素と同様に、ほとんどのブラウザは、formmethodで宣言されたフォームメソッドを上書きすることをサポートしていません。

Railsは、formmethodvalue、およびname属性の組み合わせによって、POSTを介して他のメソッドをエミュレートすることで、この問題を解決します。

<form accept-charset="UTF-8" action="/posts/1" method="post">
  <input name="_method" type="hidden" value="patch" />
  <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
  <!-- ... -->

  <button type="submit" formmethod="post" name="_method" value="delete" data-confirm="Are you sure?">Delete</button>
  <button type="submit" name="button">Update</button>
</form>

3 簡単なセレクトボックスの作成

HTMLのセレクトボックスは、選択肢ごとに1つの<option>要素を含む多くのマークアップが必要です。そのため、Railsではこの手間を減らすためのヘルパーメソッドを提供しています。

例えば、ユーザーが選択するための都市のリストがあるとします。selectヘルパーを次のように使用できます。

<%= form.select :city, ["Berlin", "Chicago", "Madrid"] %>

出力:

<select name="city" id="city">
  <option value="Berlin">Berlin</option>
  <option value="Chicago">Chicago</option>
  <option value="Madrid">Madrid</option>
</select>

また、ラベルと異なる<option>の値を指定することもできます。

<%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]] %>

出力:

<select name="city" id="city">
  <option value="BE">Berlin</option>
  <option value="CHI">Chicago</option>
  <option value="MD">Madrid</option>
</select>

このようにすることで、ユーザーは完全な都市名を表示しますが、params[:city]は「BE」、「CHI」、「MD」のいずれかになります。

最後に、selected:引数を使用してセレクトボックスのデフォルトの選択肢を指定することができます。

<%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]], selected: "CHI" %>

出力:

<select name="city" id="city">
  <option value="BE">Berlin</option>
  <option value="CHI" selected="selected">Chicago</option>
  <option value="MD">Madrid</option>
</select>

3.1 オプショングループ

場合によっては、関連するオプションをグループ化してユーザーエクスペリエンスを向上させたい場合があります。selectHash(または比較可能なArray)を渡すことで、それを行うことができます。 erb <%= form.select :city, { "ヨーロッパ" => [ ["ベルリン", "BE"], ["マドリード", "MD"] ], "北アメリカ" => [ ["シカゴ", "CHI"] ], }, selected: "CHI" %>

出力:

<select name="city" id="city">
  <optgroup label="ヨーロッパ">
    <option value="BE">ベルリン</option>
    <option value="MD">マドリード</option>
  </optgroup>
  <optgroup label="北アメリカ">
    <option value="CHI" selected="selected">シカゴ</option>
  </optgroup>
</select>

3.2 セレクトボックスとモデルオブジェクト

他のフォームコントロールと同様に、セレクトボックスはモデル属性にバインドすることができます。例えば、以下のような@personモデルオブジェクトがある場合:

@person = Person.new(city: "MD")

次のフォーム:

<%= form_with model: @person do |form| %>
  <%= form.select :city, [["ベルリン", "BE"], ["シカゴ", "CHI"], ["マドリード", "MD"]] %>
<% end %>

以下のようなセレクトボックスを出力します:

<select name="person[city]" id="person_city">
  <option value="BE">ベルリン</option>
  <option value="CHI">シカゴ</option>
  <option value="MD" selected="selected">マドリード</option>
</select>

適切なオプションが自動的にselected="selected"とマークされていることに注意してください。このセレクトボックスはモデルにバインドされているため、:selected引数を指定する必要はありませんでした!

3.3 タイムゾーンと国のセレクト

Railsでタイムゾーンのサポートを活用するには、ユーザーにどのタイムゾーンにいるか尋ねる必要があります。これには、事前定義されたActiveSupport::TimeZoneオブジェクトのリストからセレクトオプションを生成する必要がありますが、既にこれをラップしているtime_zone_selectヘルパーを使用することができます:

<%= form.time_zone_select :time_zone %>

Railsには国を選択するためのcountry_selectヘルパーがありましたが、これはcountry_selectプラグインに抽出されました。

4 日付と時間のフォームヘルパーの使用

HTML5の日付と時間の入力を使用したくない場合、Railsはプレーンなセレクトボックスをレンダリングする代替の日付と時間のフォームヘルパーを提供しています。これらのヘルパーは、各時間要素(年、月、日など)ごとにセレクトボックスをレンダリングします。例えば、以下のような@personモデルオブジェクトがある場合:

@person = Person.new(birth_date: Date.new(1995, 12, 21))

次のフォーム:

<%= form_with model: @person do |form| %>
  <%= form.date_select :birth_date %>
<% end %>

以下のようなセレクトボックスを出力します:

<select name="person[birth_date(1i)]" id="person_birth_date_1i">
  <option value="1990">1990</option>
  <option value="1991">1991</option>
  <option value="1992">1992</option>
  <option value="1993">1993</option>
  <option value="1994">1994</option>
  <option value="1995" selected="selected">1995</option>
  <option value="1996">1996</option>
  <option value="1997">1997</option>
  <option value="1998">1998</option>
  <option value="1999">1999</option>
  <option value="2000">2000</option>
</select>
<select name="person[birth_date(2i)]" id="person_birth_date_2i">
  <option value="1">1月</option>
  <option value="2">2月</option>
  <option value="3">3月</option>
  <option value="4">4月</option>
  <option value="5">5月</option>
  <option value="6">6月</option>
  <option value="7">7月</option>
  <option value="8">8月</option>
  <option value="9">9月</option>
  <option value="10">10月</option>
  <option value="11">11月</option>
  <option value="12" selected="selected">12月</option>
</select>
<select name="person[birth_date(3i)]" id="person_birth_date_3i">
  <option value="1">1</option>
  ...
  <option value="21" selected="selected">21</option>
  ...
  <option value="31">31</option>
</select>

フォームが送信されると、paramsハッシュには完全な日付を含む単一の値はありません。代わりに、"birth_date(1i)"のような特殊な名前の複数の値が存在します。Active Recordは、これらの特殊な名前の値をモデル属性の宣言されたタイプに基づいて、完全な日付または時間に組み立てる方法を知っています。そのため、このフォームが完全な日付を表す単一のフィールドを使用している場合と同様に、params[:person]Person.newPerson#updateに渡すことができます。

date_selectヘルパーに加えて、Railsはtime_selectdatetime_selectも提供しています。

4.1 個々の時間要素のためのセレクトボックス

Railsは、個々の時間要素のためのセレクトボックスをレンダリングするためのヘルパーも提供しています: select_year, select_month, select_day, select_hour, select_minute, select_secondです。これらのヘルパーは「ベア」メソッドであり、フォームビルダーのインスタンスでは呼び出されません。例えば:

<%= select_year 1999, prefix: "party" %>

以下のようなセレクトボックスを出力します:

<select name="party[year]" id="party_year">
  <option value="1994">1994</option>
  <option value="1995">1995</option>
  <option value="1996">1996</option>
  <option value="1997">1997</option>
  <option value="1998">1998</option>
  <option value="1999" selected="selected">1999</option>
  <option value="2000">2000</option>
  <option value="2001">2001</option>
  <option value="2002">2002</option>
  <option value="2003">2003</option>
  <option value="2004">2004</option>
</select>

これらのヘルパーの各々について、デフォルト値として数値の代わりに日付や時間オブジェクトを指定することもでき、適切な時間要素が抽出されて使用されます。

5 任意のオブジェクトのコレクションからの選択肢

時々、任意のオブジェクトのコレクションから選択肢のセットを生成したいことがあります。例えば、Cityモデルと対応するbelongs_to :city関連付けがある場合:

class City < ApplicationRecord
end

class Person < ApplicationRecord
  belongs_to :city
end
City.order(:name).map { |city| [city.name, city.id] }
# => [["Berlin", 3], ["Chicago", 1], ["Madrid", 2]]

その後、次のフォームでデータベースから都市を選択できるようにすることができます:

<%= form_with model: @person do |form| %>
  <%= form.select :city_id, City.order(:name).map { |city| [city.name, city.id] } %>
<% end %>

注意:belongs_to関連付けのフィールドをレンダリングする場合、関連付け自体の名前ではなく、外部キーの名前(上記の例ではcity_id)を指定する必要があります。

ただし、Railsには、コレクションから選択肢を明示的に反復処理することなく生成するためのヘルパーが用意されています。これらのヘルパーは、コレクション内の各オブジェクトに指定されたメソッドを呼び出して、各選択肢の値とテキストラベルを決定します。

5.1 collection_selectヘルパー

セレクトボックスを生成するには、collection_selectを使用できます:

<%= form.collection_select :city_id, City.order(:name), :id, :name %>

出力:

<select name="person[city_id]" id="person_city_id">
  <option value="3">Berlin</option>
  <option value="1">Chicago</option>
  <option value="2">Madrid</option>
</select>

注意:collection_selectでは、最初に値のメソッド(上記の例では:id)を指定し、次にテキストラベルのメソッド(上記の例では:name)を指定します。これは、selectヘルパーの選択肢を指定する際の順序とは逆です。

5.2 collection_radio_buttonsヘルパー

ラジオボタンのセットを生成するには、collection_radio_buttonsを使用できます:

<%= form.collection_radio_buttons :city_id, City.order(:name), :id, :name %>

出力:

<input type="radio" name="person[city_id]" value="3" id="person_city_id_3">
<label for="person_city_id_3">Berlin</label>

<input type="radio" name="person[city_id]" value="1" id="person_city_id_1">
<label for="person_city_id_1">Chicago</label>

<input type="radio" name="person[city_id]" value="2" id="person_city_id_2">
<label for="person_city_id_2">Madrid</label>

5.3 collection_check_boxesヘルパー

チェックボックスのセットを生成するには、collection_check_boxesを使用できます。例えば、has_and_belongs_to_many関連付けをサポートするために使用できます:

<%= form.collection_check_boxes :interest_ids, Interest.order(:name), :id, :name %>

出力:

<input type="checkbox" name="person[interest_id][]" value="3" id="person_interest_id_3">
<label for="person_interest_id_3">Engineering</label>

<input type="checkbox" name="person[interest_id][]" value="4" id="person_interest_id_4">
<label for="person_interest_id_4">Math</label>

<input type="checkbox" name="person[interest_id][]" value="1" id="person_interest_id_1">
<label for="person_interest_id_1">Science</label>

<input type="checkbox" name="person[interest_id][]" value="2" id="person_interest_id_2">
<label for="person_interest_id_2">Technology</label>

6 ファイルのアップロード

一般的なタスクとして、人物の写真やデータを処理するためのCSVファイルなど、ある種のファイルをアップロードすることがあります。ファイルのアップロードフィールドは、file_fieldヘルパーを使用してレンダリングできます。

<%= form_with model: @person do |form| %>
  <%= form.file_field :picture %>
<% end %>

ファイルのアップロードに関して最も重要なことは、レンダリングされるフォームのenctype属性を「multipart/form-data」に設定する必要があることです。これは、form_with内でfile_fieldを使用すると自動的に行われます。また、属性を手動で設定することもできます:

<%= form_with url: "/uploads", multipart: true do |form| %>
  <%= file_field_tag :picture %>
<% end %>

form_withの規則に従って、上記の2つのフォームのフィールド名も異なることに注意してください。つまり、最初のフォームのフィールド名はperson[picture]params[:person][:picture]でアクセス可能)になり、2番目のフォームのフィールド名は単にpictureparams[:picture]でアクセス可能)になります。

6.1 アップロードされるもの

paramsハッシュ内のオブジェクトはActionDispatch::Http::UploadedFileのインスタンスです。次のスニペットは、アップロードされたファイルを元のファイルと同じ名前で#{Rails.root}/public/uploadsに保存します。

def upload
  uploaded_file = params[:picture]
  File.open(Rails.root.join('public', 'uploads', uploaded_file.original_filename), 'wb') do |file|
    file.write(uploaded_file.read)
  end
end

ファイルがアップロードされた後、ディスク、Amazon S3などにファイルを保存する場所、モデルとの関連付け、画像ファイルのリサイズ、サムネイルの生成など、さまざまなタスクがあります。これらのタスクをサポートするために、Active Storageが設計されています。

7 フォームビルダのカスタマイズ

form_withfields_forによって生成されるオブジェクトはActionView::Helpers::FormBuilderのインスタンスです。フォームビルダは、単一のオブジェクトのフォーム要素を表示する概念をカプセル化しています。通常の方法でフォームのヘルパーを作成することもできますが、ActionView::Helpers::FormBuilderのサブクラスを作成し、そこにヘルパーを追加することもできます。例えば、

<%= form_with model: @person do |form| %>
  <%= text_field_with_label form, :first_name %>
<% end %>

は、次のように置き換えることができます。

<%= form_with model: @person, builder: LabellingFormBuilder do |form| %>
  <%= form.text_field :first_name %>
<% end %>

以下のようなLabellingFormBuilderクラスを定義することで可能です。

class LabellingFormBuilder < ActionView::Helpers::FormBuilder
  def text_field(attribute, options = {})
    label(attribute) + super
  end
end

これを頻繁に再利用する場合は、builder: LabellingFormBuilderオプションを自動的に適用するlabeled_form_withヘルパーを定義することができます。

def labeled_form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
  options[:builder] = LabellingFormBuilder
  form_with model: model, scope: scope, url: url, format: format, **options, &block
end

また、使用するフォームビルダによって次のような動作が決まります。

<%= render partial: f %>

もしfActionView::Helpers::FormBuilderのインスタンスであれば、これはformパーシャルをレンダリングし、パーシャルのオブジェクトをフォームビルダに設定します。フォームビルダがLabellingFormBuilderクラスであれば、labelling_formパーシャルが代わりにレンダリングされます。

8 パラメータの命名規則の理解

フォームからの値は、paramsハッシュのトップレベルにあるか、別のハッシュにネストされることがあります。例えば、Personモデルの標準的なcreateアクションでは、params[:person]は通常、作成するPersonのすべての属性のハッシュです。paramsハッシュには配列、ハッシュの配列などが含まれることもあります。

基本的にHTMLフォームは、構造化されたデータについては何も知りません。生成されるのは名前と値のペアであり、ペアは単なる文字列です。アプリケーションで見る配列やハッシュは、Railsが使用するパラメータの命名規則の結果です。

8.1 基本的な構造

基本的な構造は配列とハッシュの2つです。ハッシュはparamsで値にアクセスするために使用される構文を反映しています。例えば、フォームに次のような要素が含まれている場合、

<input id="person_name" name="person[name]" type="text" value="Henry"/>

paramsハッシュには次のようになります。

{ 'person' => { 'name' => 'Henry' } }

そして、コントローラでparams[:person][:name]を使用すると、送信された値を取得することができます。

ハッシュは必要なだけネストすることができます。例えば、

<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>

とすると、paramsハッシュは次のようになります。

{ 'person' => { 'address' => { 'city' => 'New York' } } }

通常、Railsは重複するパラメータ名を無視します。パラメータ名が空の角括弧[]で終わる場合、それらは配列に蓄積されます。ユーザーが複数の電話番号を入力できるようにしたい場合、次のようにフォームに配置することができます。

<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>

これにより、params[:person][:phone_number]は入力された電話番号を含む配列になります。

8.2 組み合わせる

これらの2つの概念を組み合わせることができます。ハッシュの要素の1つは、前の例のような配列である場合もあります。また、ハッシュの配列を持つこともできます。例えば、フォームでは次のように繰り返しアドレスを作成できるようにすることができます。

<input name="person[addresses][][line1]" type="text"/>
<input name="person[addresses][][line2]" type="text"/>
<input name="person[addresses][][city]" type="text"/>
<input name="person[addresses][][line1]" type="text"/>
<input name="person[addresses][][line2]" type="text"/>
<input name="person[addresses][][city]" type="text"/>

これにより、params[:person][:addresses]は、line1line2cityをキーとするハッシュの配列になります。

ただし、制限があります。ハッシュは任意のレベルでネストできますが、「配列のレベル」は1つだけ許可されます。通常、配列はハッシュに置き換えることができます。例えば、モデルオブジェクトの配列の代わりに、id、配列のインデックス、または他のパラメータをキーとするモデルオブジェクトのハッシュを持つことができます。 警告:配列パラメータはcheck_boxヘルパーと互換性がありません。HTML仕様によると、チェックされていないチェックボックスは値を送信しません。しかし、チェックボックスが常に値を送信するのが便利な場合があります。check_boxヘルパーは、同じ名前の補助的な隠し入力を作成することでこれを偽装します。チェックボックスがチェックされていない場合、隠し入力のみが送信され、チェックされている場合は両方が送信されますが、チェックボックスによって送信される値が優先されます。

8.3 fields_forヘルパーの:indexオプション

例えば、個人の各住所に対してフィールドのセットを含むフォームをレンダリングしたいとします。fields_forヘルパーとその:indexオプションを使用すると、次のように補助できます:

<%= form_with model: @person do |person_form| %>
  <%= person_form.text_field :name %>
  <% @person.addresses.each do |address| %>
    <%= person_form.fields_for address, index: address.id do |address_form| %>
      <%= address_form.text_field :city %>
    <% end %>
  <% end %>
<% end %>

IDが23と45の2つの住所を持つ場合、上記のフォームは次のような出力を生成します:

<form accept-charset="UTF-8" action="/people/1" method="post">
  <input name="_method" type="hidden" value="patch" />
  <input id="person_name" name="person[name]" type="text" />
  <input id="person_address_23_city" name="person[address][23][city]" type="text" />
  <input id="person_address_45_city" name="person[address][45][city]" type="text" />
</form>

これにより、次のようなparamsハッシュが生成されます:

{
  "person" => {
    "name" => "Bob",
    "address" => {
      "23" => {
        "city" => "Paris"
      },
      "45" => {
        "city" => "London"
      }
    }
  }
}

フォームのすべての入力は、person_formフォームビルダーでfields_forを呼び出したため、"person"ハッシュにマップされます。また、index: address.idを指定することで、各都市の入力のname属性をperson[address][#{address.id}][city]としてレンダリングしました。そのため、paramsハッシュを処理する際に変更する必要があるAddressレコードを特定できます。

indexオプションには他の重要な数値や文字列を渡すこともできます。さらに、配列パラメータを生成するにはnilを渡すこともできます。

より複雑なネストを作成するには、入力名の先頭部分を明示的に指定することもできます。例えば:

<%= fields_for 'person[address][primary]', address, index: address.id do |address_form| %>
  <%= address_form.text_field :city %>
<% end %>

次のような入力を作成します:

<input id="person_address_primary_23_city" name="person[address][primary][23][city]" type="text" value="Paris" />

text_fieldなどのヘルパーに直接indexオプションを渡すこともできますが、個々の入力フィールドではなくフォームビルダーレベルで指定する方が繰り返しを減らせる場合が多いです。

一般的に、最終的な入力名は、fields_for / form_withに指定された名前、indexオプションの値、および属性の名前の連結になります。

最後に、:indexにIDを指定する代わりに(例:index: address.id)、指定された名前に"[]"を追加するショートカットも使用できます。例えば:

<%= fields_for 'person[address][primary][]', address do |address_form| %>
  <%= address_form.text_field :city %>
<% end %>

元の例とまったく同じ出力が生成されます。

9 外部リソースへのフォーム

Railsのフォームヘルパーは、外部リソースにデータを投稿するためのフォームを作成するためにも使用できます。ただし、リソースにauthenticity_tokenを設定する必要がある場合があります。これは、form_withオプションにauthenticity_token: 'your_external_token'パラメータを渡すことで行うことができます:

<%= form_with url: 'http://farfar.away/form', authenticity_token: 'external_token' do %>
  フォームの内容
<% end %>

支払いゲートウェイなどの外部リソースにデータを送信する場合、フォームで使用できるフィールドは外部APIによって制限される場合があり、authenticity_tokenを生成することは望ましくない場合があります。トークンを送信しないようにするには、:authenticity_tokenオプションにfalseを渡すだけです:

<%= form_with url: 'http://farfar.away/form', authenticity_token: false do %>
  フォームの内容
<% end %>

10 複雑なフォームの作成

多くのアプリケーションは、単一のオブジェクトのフォーム編集を超えて成長します。たとえば、Personを作成する際には、ユーザーに複数の住所レコード(自宅、職場など)を同じフォームで作成させたい場合があります。その後、その人物を編集する際には、必要に応じて住所を追加、削除、または修正できるようにする必要があります。

10.1 モデルの設定

Active Recordは、accepts_nested_attributes_forメソッドを介してモデルレベルのサポートを提供します。

class Person < ApplicationRecord
  has_many :addresses, inverse_of: :person
  accepts_nested_attributes_for :addresses
end

class Address < ApplicationRecord
  belongs_to :person
end

これにより、Personaddresses_attributes=メソッドが作成され、住所を作成、更新、(オプションで)削除できるようになります。

10.2 ネストされたフォーム

次のフォームでは、ユーザーがPersonと関連する住所を作成できます。

<%= form_with model: @person do |form| %>
  Addresses:
  <ul>
    <%= form.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>

        <%= addresses_form.label :street %>
        <%= addresses_form.text_field :street %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

関連オブジェクトがネストされた属性を受け入れる場合、fields_forは関連の要素ごとにそのブロックを一度だけレンダリングします。特に、人物に住所がない場合は何もレンダリングされません。一般的なパターンは、コントローラーが少なくとも1つのフィールドセットをユーザーに表示するために、1つ以上の空の子を作成することです。以下の例では、新しい人物のフォームに2つの住所フィールドセットがレンダリングされます。

def new
  @person = Person.new
  2.times { @person.addresses.build }
end

fields_forはフォームビルダーを生成します。パラメータの名前は、accepts_nested_attributes_forが期待するものになります。たとえば、2つの住所を持つユーザーを作成する場合、送信されるパラメータは次のようになります。

{
  'person' => {
    'name' => 'John Doe',
    'addresses_attributes' => {
      '0' => {
        'kind' => 'Home',
        'street' => '221b Baker Street'
      },
      '1' => {
        'kind' => 'Office',
        'street' => '31 Spooner Street'
      }
    }
  }
}

:addresses_attributesハッシュのキーの実際の値は重要ではありませんが、それらは整数の文字列である必要があり、各住所ごとに異なる値である必要があります。

関連オブジェクトが既に保存されている場合、fields_forは保存されたレコードのidを持つ非表示の入力を自動生成します。これを無効にするには、fields_forinclude_id: falseを渡します。

10.3 コントローラー

通常通り、モデルに渡す前にコントローラーで許可されたパラメータを宣言する必要があります。

def create
  @person = Person.new(person_params)
  # ...
end

private
  def person_params
    params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
  end

10.4 オブジェクトの削除

accepts_nested_attributes_forallow_destroy: trueを渡すことで、関連オブジェクトの削除を許可できます。

class Person < ApplicationRecord
  has_many :addresses
  accepts_nested_attributes_for :addresses, allow_destroy: true
end

オブジェクトの属性ハッシュに_destroyキーが含まれ、その値がtrue(たとえば、1、'1'、true、または'true')に評価される場合、オブジェクトは削除されます。このフォームでは、ユーザーは住所を削除できます。

<%= form_with model: @person do |form| %>
  Addresses:
  <ul>
    <%= form.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.check_box :_destroy %>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

コントローラーの許可されたパラメータも更新して、_destroyフィールドを含めるように忘れないでください。

def person_params
  params.require(:person).
    permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])
end

10.5 空のレコードの防止

ユーザーが入力していないフィールドセットを無視することは、しばしば便利です。これは、accepts_nested_attributes_for:reject_ifプロックを渡すことで制御できます。このプロックは、フォームによって送信された属性のハッシュごとに呼び出されます。プロックがtrueを返す場合、Active Recordはそのハッシュの関連オブジェクトを構築しません。以下の例では、kind属性が設定されている場合にのみ住所を構築しようとします。 ruby class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses, reject_if: lambda { |attributes| attributes['kind'].blank? } end

便利のために、代わりにシンボル :all_blank を渡すこともできます。これにより、すべての属性が空であるレコード(_destroy の値を除く)は拒否されるプロックが作成されます。

10.6 フィールドの動的追加

あらかじめ複数のフィールドセットをレンダリングする代わりに、ユーザーが「新しい住所を追加」ボタンをクリックしたときにのみ追加することもできます。Rails はこれに対して組み込みのサポートを提供していません。新しいフィールドセットを生成する際には、関連する配列のキーが一意であることを確認する必要があります - 現在の JavaScript 日付(エポックからのミリ秒)は一般的な選択肢です。

11 フォームビルダーのコンテキスト外でタグヘルパーを使用する

フォームビルダーのコンテキスト外でフォームフィールドをレンダリングする必要がある場合、Rails は一般的なフォーム要素のためのタグヘルパーを提供しています。たとえば、check_box_tag

<%= check_box_tag "accept" %>

出力:

<input type="checkbox" name="accept" id="accept" value="1" />

一般的に、これらのヘルパーは、フォームビルダーの対応するものと同じ名前に _tag 接尾辞が付いたものです。完全なリストについては、FormTagHelper API ドキュメントを参照してください。

12 form_tagform_for の使用

Rails 5.1 で form_with が導入される前は、その機能は form_tagform_for に分割されていました。いずれも現在はソフト非推奨となっています。使用方法についてのドキュメントは、このガイドの古いバージョンで見つけることができます。

フィードバック

このガイドの品質向上にご協力ください。

タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。 開始するには、ドキュメントへの貢献セクションを読んでください。

不完全なコンテンツや最新でない情報も見つかるかもしれません。 メインのドキュメントに不足しているドキュメントを追加してください。 修正済みかどうかは、まずEdge Guidesを確認してください。 スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。

修正すべき点を見つけたが、自分で修正できない場合は、 問題を報告してください

そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。