対象読者(学習前の自分)
- rails初学者
- form_withヘルパーを魔術に感じている方
form_withヘルパーでできること
フォームの自動生成。
こんなコードを書くと...
<%= form_with(model: @user) do |form| %>
<%= form.label :name %>
<%= form.text_field :name, class: 'form-control' %>
<%= form.label :email %>
<%= form.email_field :email, class: 'form-control' %>
<%= form.label :password %>
<%= form.password_field :password, class: 'form-control' %>
<%= form.label :password_confirmation, "Confirmation" %>
<%= form.password_field :password_confirmation, class: 'form-control' %>
<%= form.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
form_withヘルパーとしての主な機能はこれ=裏でhtml記述を自動生成してくれている だけ。
form_withヘルパーの使い方
どの記述で何をしているのか。
本体
<%= form_with(model: @user) do |f| %>
-
"model"オプション
- フォームを表示する画面に渡ってきたインスタンスを受け取る。ここではuserインスタンス
- 編集画面で登録済みユーザーの情報をデフォルト値として入れておける
- ログイン後に訪れる編集画面(/users/:id/edit)ではuserインスタンスが渡ってくる
- →渡ってきたインスタンスを使ってデフォルト値埋め
- 新規登録画面(/users/new)では、インスタンスは渡ってこないので
@user
はnil
- ログイン後に訪れる編集画面(/users/:id/edit)ではuserインスタンスが渡ってくる
-
do {form}
- ブロック。この記述により、ブロック内で
form.xxx
の形で便利な各種メソッドを呼び出せる(form_withヘルパーの理解にあたっては、ブロックを必ずしもちゃんと理解している必要はない)
- ブロック。この記述により、ブロック内で
label
<%= f.label :name %>
input定義
<%= form.text_field :name, class: 'form-control' %>
- form.text_field
- フォーム要素の種別を指定できる
- text_field:1行のテキストボックス
- email_field:メールアドレス
- password_field:パスワード
- etc...もちろんラジオボタンやチェックボックスなども設定可能
- フォーム要素の種別を指定できる
- :name
- フォームの送信先となるcontrollerのcreate/updateメソッドの引数名↓、及びモデルのパラメータ名(DBのカラム名)と一致させる
- これにより以下が実現できる
- フォームの送信をcontrollerで受け取り、DBを更新する
- DBのデータでフォーム要素をデフォルト値埋めする(編集画面のみ)
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
フォーム送信先は自動で選択
フォームがsubmitされたとき、controllerのcreateメソッドへのPOSTリクエストか、updateメソッドへのPATCHリクエスト、適切な方を自動で選択してくれます。
form_withヘルパーの仕組み
html要素の自動生成
例えば、ここの記述で
<%= form_with(model: @user) do |form| %>
<%= form.label :name %>
<%= form.text_field :name, class: 'form-control' %>
~~~
このようなHTMLが生成される
<label for="user_name">Name</label>
<input class="form-control" type="text" value="Example User" name="user[name]" id="user_name">
これを見ると、
- form.label によりlabel要素が、
- form.text_fieldによりinput要素(type="text") が生成されていること
がわかる。さらに、
-
form_with(model: *@xxx*)
で指定した値が、label要素のfor属性、input要素のname/id属性に反映されていること -
form.label :*xxx*
がfor属性に反映されていること -
form.text_field :*xxx*
がname/id属性に反映されていること
もわかる。
なので例えば、下記のように書き換えをすると
<%= form_with(model: @user) do |form| %>
<%= form.label :name %>
<%= form.text_field :★適当な名前★, class: 'form-control' %>
生成されるhtmlもそれに合わせて変わることになる。
<label for="user_name">Name</label>
<input class="form-control" type="text" name="user[★適当な名前★]" id="user_★適当な名前★">
指定する値の名前をcontrollerの引数(DBのカラム名)に合わせる必要があるのは、ここの名前がそのままname属性に渡ってしまうからということですね。
フォーム送信先の自動選択
modelにインスタンスが渡ってきているかで判断してくれる。
- 新規登録画面=modelオプションにインスタンスが渡ってきていない場合はcontroller#createへPOSTリクエスト
- 編集画面=modelオプションにインスタンスが渡ってきている場合はcontroller#updateへPATCHリクエスト
という形で送り分けてくれる。
<form action="/users" accept-charset="UTF-8" method="post">
ユーザー編集画面のフォーム
<form action="/users/1" accept-charset="UTF-8" method="post">
<input type="hidden" name="_method" value="patch" autocomplete="off">
※ HTMLのform要素はGETとPOSTにしか対応していないため、PATCHリクエストするには工夫が必要。railsはname=_methodを用いてサーバ側にリクエストするメソッドを設定可能
何が嬉しいのか
所感に近いですが、上記をふまえて以下あたりはありがたいなと思いました。
- 命名規則に従っていればフォーム送信、デフォルト値埋めが何も考えずともできる(これがメイン)
- 各フォーム要素、種別が違ってもほぼ同じ書き方ができるので可読性が上がる
- 記述量が減るのでタイポが減る
その他
model以外にも様々なオプションがある。
https://railsdoc.com/page/form_with
一部抜粋
オプション | 例 | 効果 |
---|---|---|
scope | scope: session | modelを指定しない場合には指定の必要あり。 controller#createにデータを読み取ってもらうのに使う(name属性のプレフィックスになる)。 例の場合だと、controller側にて session.xxx でパラメータを読み取れる |
url | url: login_path | modelを指定しない場合に指定の必要あり。 サブミット先のパスを指定する。 modelを指定している場合は(命名規則に基づき)、該当のモデルの/newないし/editのパスを自動で設定してくれている。 |
method | mthod: post | サブミット時のHTTPメソッドを指定する。 普通のmvcの場合はviewがnewかeditかでpostかputか汲み取ってくれるので不要 |
参考
モデルなどからフォームタグを生成 - Railsドキュメント
Railsでform_withでログインする
form_withのlocal: trueって必要なん?これ何なん?(Ruby on Rails)
さいごに
間違いやより正しい表現等を見つけていただけましたら、ご指摘いただけるとありがたいですmm