【Rails入門説明書】form_forについて解説
form_forヘルパーを使うことで、フォームを簡単に設置することができます。
そのため、多くのRailsアプリケーションで使われています。
しかし、多くの人がform_forヘルパーがどんな動きでフォームを作っているのかをあまり理解しないままに使ってしまっています。
もし曖昧なまま使っているのであれば、ぜひこの記事を読んでみてください!
また、初めてform_forヘルパーを使ったViewファイルを見て、「よく分からない」という人はチャンスです!
これを機会に、理解しておくことをお勧めします。
form_forヘルパーは、Rails5.1から非推奨(将来的になくなる可能性がある)になりました。
Rails5.1以降にform_forヘルパーの代わりとなるform_withヘルパーについては、「【Rails入門説明書】form_withについて解説」を参照してください。
ただし、form_forヘルパーを理解してからの方が分かりやすいので、まずは本記事を読まれることをおすすめします。
form_forを読み解く
実際のプログラムコードを見ながら、form_forがどういった動きをしてフォームを作っているのかを解説していきます。
環境作成
scaffoldを使えば、簡単に具体例を作れますので、まずはテスト用の環境を作ります。コマンドプロンプトで以下のコマンドを順に実行してください。
※詳細は、「【Rails入門説明書】scaffoldについて解説」を確認してください。
$rails new form_test
$ cd form_test
$ rails generate scaffold User name:string age:integer email:string password:string note:text
$ rake db:migrate
$ rails c
User.create(name:"Arioka", age:30, email:"dai@hese.jp", password:"0000", note:"member")
User.create(name:"Yamada", age:28, email:"ryo@hese.jp", password:"1111", note:"top")
exit
お使いのRailsバージョンが5.1以上の場合は、form_withメソッドが使われます。
そのため、Viewのフォームをしている_form.html.erbの1行目を修正しておいてください。
(app/views/users/_form.html.erb)
・変更前
<%= form_with(model: user, local: true) do |form| %>
・変更後
<%= form_for(user) do |form| %>
念のため、サーバーを起動し、ブラウザで「localhost:3000/users」にアクセスしてみましょう。
フォームの確認
まずは、scaffoldで作成したフォームapp/views/users/_form.html.erbを見てみましょう。
少し長いですが、プログラムコードと、生成されるHTMLの抜粋が以下です。
(app/views/users/_form.html.erb)
<%= form_for(user) do |form| %> <!-- (1) -->
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %> <!-- (2) -->
<%= form.text_field :name %> <!-- (3) -->
</div>
<div class="field">
<%= form.label :age %> <!-- (2) -->
<%= form.number_field :age %> <!-- (4) -->
</div>
<div class="field">
<%= form.label :email %> <!-- (2) -->
<%= form.text_field :email %> <!-- (5) -->
</div>
<div class="field">
<%= form.label :password %> <!-- (2) -->
<%= form.text_field :password %> <!-- (6) -->
</div>
<div class="field">
<%= form.label :note %> <!-- (2) -->
<%= form.text_area :note %> <!-- (7) -->
</div>
<div class="actions">
<%= form.submit %> <!-- (8) -->
</div>
<% end %>
form_forヘルパーそのものは(1)の行で使われ、form_forヘルパーのブロック引数formが得られています。
このブロック引数formは、form_forヘルパーに渡されているモデルオブジェクトの情報を保持するFormBuilderオブジェクトです。
そして、FormBuilderオブジェクトのメソッドを使っているのが、(2)から(8)の部分です。この点を踏まえて、生成されるHTMLを見てみます。
(※FormBuilderはHelperを継承していますので、Helperのメソッドも使えます)
(新規作成画面で生成されるHTMLの抜粋)
<span><form action="/users" method="post"></span>
<div class="field">
<label for="user_name">Name</label> <!-- (2) -->
<input type="text" name="user[name]" id="user_name" /> <!-- (3) -->
</div>
<div class="field">
<label for="user_age">Age</label> <!-- (2) -->
<input type="number" name="user[age]" id="user_age" /> <!-- (4) -->
</div>
<div class="field">
<label for="user_email">Email</label> <!-- (2) -->
<input type="text" name="user[email]" id="user_email" /> <!-- (5) -->
</div>
<div class="field">
<label for="user_password">Password</label> <!-- (2) -->
<input type="text" name="user[password]" id="user_password" /> <!-- (6) -->
</div>
<div class="field">
<label for="user_note">Note</label> <!-- (2) -->
<textarea name="user[note]" id="user_note"></textarea> <!-- (7) -->
</div>
<div class="actions">
<input type="submit" name="commit" value="Create User" data-disable-with="Create User" /> <!-- (8) -->
</div>
</form>
プログラムコードの(1)から(8)が、HTMLの(1)から(8)の部分に該当しています。
(1)の部分を比較すると、form_forヘルパーによってフォームが生成されています。
そして、(2)から(8)のFormBuilderオブジェクトのメソッドが、フォームの各部品(フォームコントロール)を生成しているわけです。
form_forが生成するフォーム
もう少し、form_forヘルパーがどうやってフォームを生成しているのか、プログラムコードと生成されるHTMLを並べて、確認してみましょう。
(app/views/users/_form.html.erb)
<%= form_for(user) do |form| %> <!-- (1) -->
<span><form action="/users" method="post"> <!-- (1) --></span>
プログラムコードの「do |form|」については、ブロック指定ですのでここでは無視します。
すると、「form_for(user)」がここでのform_forヘルパーのコードということになります。
たったこれだけのコードだけで、適切なフォームが生成されます。
Railsの自動推測によって、form_forヘルパーに渡されたモデルオブジェクト(今回はuser)から、HTMLのactionとmethodの値を推測してくれているのです。
今回は新規作成であるため、userはnewされたばかりで、値が入っていません。
そのため、新規作成用のフォームが生成されています。
なお、編集画面の場合のHTMLは以下です。
<span>
<form action="/users/1" method="post"> <!-- (1) --></span>
プログラムコードは同じですが、userオブジェクトが値を持っているかどうかで、classやid、actionの値が適切に変わっています。
FormBuilderオブジェクトのメソッドが生成するフォームコントロール
次に、FormBuilderオブジェクトのメソッドがどんな解釈をされて、フォームコントロールが作られているのかを見ていきましょう。
(app/views/users/_form.html.erb)
<%= form.label :name %> <!-- (2) -->
<%= form.text_field :name %> <!-- (3) -->
<label for="user_name">Name</label> <!-- (2) -->
<input type="text" name="user[name]" id="user_name" /> <!-- (3) -->
(3)のテキストボックスについては、text_fieldメソッドが生成してくれます。
引数として渡しているシンボルから格納すべき値を推測しますが、このとき、メソッドを利用しているFormBuilderオブジェクトが保持しているモデルの情報に紐づけ、user[name]を格納先に指定してくれます。(idも同様)
ラベルについても同じく、labelメソッドの引数から関連付けるコントロールのidが推測されています。
なお、form_forヘルパーが生成したフォームで入力した値については、コントロールからparams[モデル名][カラム名]のハッシュ(ここでは、params[user][name])でアクセスすることができます。
非公開: 「人のために働きたい」を実現【警察官からエンジニア転職】form_forの詳細情報
Railsの基本的な動き通り、form_forヘルパーもシンボル名などから推測して処理を追加してくれます。
とても便利ではあるのですが、それでは思ったような動きができないことがあります。そんな場合にカスタマイズできるオプション設定がありますので、form_forヘルパーの構文とともに紹介しましょう。
構文
$ form_for(モデルオブジェクト , オプション) do |f|
フォームコントロールの設置
$ end
オプションには、以下があります。
オプション | 説明 | デフォルト |
---|---|---|
:url | フォームの送信先 | 現在のURL |
:namespace | 名前空間の設定をしているコントローラへデータを渡したい場合の名前空間 | 名前空間なし |
:method | HTTPリクエスト | POST |
:authenticity_token | クロスサイト・リクエストフォージェリ(CSRF)という脆弱性に対策するための認証トークンの無効化や変更 | デフォルトトークン |
:remote | trueを指定すると、Unobtrusive JavaScript(控えめなJavaScript)で送信する情報を制御できる | false(Ajaxでの送信) |
:enforce_utf8 | IE5以前のブラウザの文字化け対策用に埋め込むHiddenパラメータの出力設定 true:出力 false:出力しない |
true |
:html | フォームコントロールにHTML属性をつける | 属性なし |
:as | モデルの名称を一時的に設定する | モデル名 |
テスト環境のform_forヘルパー(app/views/users/_form.html.erb)について、敢えてオプション設定を使って記載すると、以下のようになります。
<%= form_for user,
as: :user,
url: users_path,
html: { class: "new_user", id: "new_user" } do |form| %>
userがnewしたばかりの場合、Railsは裏でこのような設定を行って処理しているのです。
参考として、編集の場合は、以下のようになるでしょう。
<%= form_for user,
as: :user,
url: user_path(user),
method: :patch,
html: { class: "edit_user", id: "edit_user_1" } do |form| %>
この形を憶えておくと、オプションによるカスタマイズがやりやすいかもしれません。
なおオプション設定の中で「:urlオプション」と「:namespaceオプション」、「:htmlオプション」の3つは比較的よく使いますので少し詳しく説明しておきましょう。
フォームで入力された情報の送信先を変える
「:url」オプションを使えば、入力データの送信先を変更できます。
例えば、新規登録画面で入力したデータで更新する場合のプログラムコードを紹介しましょう。(新規登録したデータが、登録済みの人の場合に、更新するといった場合に必要になるかもしれません)
form_forヘルパーに、以下のようにオプションを追加してみましょう。
(app/views/users/_form.html.erb)
<!-- <%= @user = User.find(1) %> -->
<%= form_for(user,
url: user_path(@user),
method: :patch,
html: { class: "edit_user", id: "edit_user_1" }) do |form| %> <!-- (1) -->
なお、上書きする既存のデータを指定するため、変数@userを追加しています。
「localhost:3000/users/new」にアクセスして[Create User]をクリックしてもデータは3つのままで、追加しようとした情報が1つ目のデータに上書きされます。
フォームにHTML属性をつける
フォームに自動で付与されるidやclassなどを変更したり、他のHTML属性をつけたりする場合に使うのが「:html」オプションです。
「:html」オプションに、ハッシュでHTML属性を渡すことで、フォームにHTML属性を設定することができます。
(app/views/users/_form.html.erb)
html.erb
<%= form_for(user,
html: { class: "test_form", id: "test_form" }) do |form| %> <!-- (1) -- >
見た目は変わりませんが、HTMLは以下のようになっています。(抜粋です)
<form class="test_form" id="test_form" action="/users" method="post"> <!-- (1) -->
別の名前空間のコントローラへフォームで入力された情報を送信する
例えば同じ商品テーブルからデータを表示する際、会員と非会員で画面を変えたい場合などは、それぞれ別のディレクトリを作って、コントローラもViewも別に管理した方が、メンテナンス性は高いでしょう。
そのような場合、ルーティングでディレクトリ名の名前空間を設定して対応します。
そうすることで、Railsがディレクトリを推測して、正しいコントロール(アクション)へアクセスするURLを生成してくれるのです。
form_forが生成するフォームコントロール
form_forヘルパーのブロック引数として得られるFormBuilderオブジェクトのメソッドが、フォームコントロールを生成します。
このとき、メソッドによって生成されるフォームコントロールが違いますので、必要なメソッドを知らなければいけません。
ここでは、メソッドの種類と生成されるフォームコントロールについて、紹介しましょう。
メソッド | 説明 | オプション(入力対象カラムの後の引数) |
---|---|---|
check_box | チェックボックス | HTML属性を設定可能 |
color_field | 色の入力欄 | HTML属性を設定可能 |
date_field | 日付の入力欄 | デフォルト値とHTML属性を設定可能 |
datetime_field | 日時の入力欄(グローバルタイム) | HTML属性を設定可能 |
datetime_local_field | 日時の入力欄(ローカルタイム) | HTML属性を設定可能 |
email_field | emailアドレスの入力欄 | HTML属性を設定可能 |
fields_for | form_forの中で別のモデルを指定したフォーム | form_forと同じ |
file_field | 画像や文章などのファイルを選択するフォーム | HTML属性を設定可能 |
hidden_field | 隠しフィールドの生成 | HTML属性を設定可能 |
label | ラベルの生成 | HTML属性を設定可能 |
month_field | 月の入力欄 | 最小値(:min)、最大値(:max)、粒度(:max)、HTML属性を設定可能 粒度は、数字を増減させるためのボタンで増減される値 |
number_field | 数値入力欄 数字以外は入力不可能 |
HTML属性を設定可能 |
password_field | パスワード入力欄 入力された文字は自動的に*表示(マスク)される |
HTML属性を設定可能 |
phone_field telephone_field |
電話番号入力欄 | HTML属性を設定可能 |
radio_button | ラジオボタンの生成 | 表示文言、チェックの有無(checked)、HTML属性を設定可能 |
range_field | 範囲選択バー | HTML属性を設定可能 |
search_field | 検索ボックスフォーム | HTML属性を設定可能 |
text_area | 文字列入力欄 改行込みの文字列 |
HTML属性を設定可能 |
text_field | 文字列入力欄 | HTML属性を設定可能 |
time_field | 時間入力欄 | 最小値(:min)、最大値(:max)、粒度(:max)、HTML属性を設定可能 粒度は、数字を増減させるためのボタンで増減される値 |
url_field | URL入力欄 | HTML属性を設定可能 |
week_field | 週の入力欄 | 最小値(:min)、最大値(:max)、粒度(:max)、HTML属性を設定可能 粒度は、数字を増減させるためのボタンで増減される値 |
submit | 送信ボタン | 表示文言、HTML属性を設定可能 入力対象カラムは不要 表示文言を省略すると、モデルが保存済みかどうかで文言を変更する (保存済み:「Update モデル名」 未保存:「Create モデル名」) |
まとめ
form_forヘルパーについて、お伝えしました。
form_forヘルパーを利用すれば、ユーザー登録や編集などのフォームが手軽に実装でき、Viewファイルのプログラムコードもすっきりと分かりやすいものになります。
Railsの特徴である名称による推測でプログラムコードが自動生成されますので、URLの管理などもほとんど行う必要がなく、メンテナンス性も高いと言えるでしょう。
その反面自由度が低く、オプションを駆使してカスタマイズできなければ思ったような管理ができなくなりますので、しっかりと学習していきましょう。
ただし、冒頭にも書いたとおり、Rails5.1からはform_forは非推奨(将来的になくなる可能性がある)になりましたので、form_forヘルパーの代わりとなるform_withヘルパーについても、並行して学習を進めるようにしてください。
なお、form_withヘルパーについては、別の記事「【Rails入門説明書】form_withについて解説」で詳しく説明してますので、ぜひお読みください。
・form_forヘルパーで、フォームを簡単に設置できる
・form_forヘルパーで得られるFormBuilderオブジェクトのメソッドがフォームコントロールを生成する
・form_forヘルパーのオプションで、入力データの送信先を変更できる
・form_forヘルパーは、Rails5.1から非推奨(将来的になくなる可能性がある)になる。
・Rails5.1以降は、form_withヘルパーを使う
【インタビュー】1ヶ月でRubyをゼロから学び、Webエンジニアとして転職!
ブラジルから帰国し技術をつけようとRubyエンジニアを目指してWebCampでRubyを学び、見事Webエンジニアとして転職を果たした田中さんにお話を伺いました。
「Rubyの学習がしたい。基礎をしっかりと理解したい」
「転職のサポートがほしい」
と考えている方はぜひお読み下さい。
https://web-camp.io/magazine/archives/8535