概要
form系のヘルパーの基本的な機能・挙動について整理してみた。
- form系の新旧4つのヘルパーの動作についてまとめた
- モデルを介して複数のパラメーターを扱う
form_for
とform_with
のオーソドックスな使い方については、最後の「パラメーターを一括で扱う」にまとめた。
各formヘルパーの関係は
form_tag
、form_for
、form_with
はHTMLのform
タグを生成するRailsのヘルパー- Rails 5.1より前は
form_tag
とform_for
- Rails 5.1から
form_with
が導入され、form_tag
とform_for
は非推奨に
form_tag
とform_for
については
- f
orm_tag
はブロック内にtext_field_tag
などtag系のヘルパーを持つことを想定 - データベースと紐づいたモデルを扱う場合は
form_for
、モデルを介さずにフォームのデータのみやりとりする場合はform_tag
を使用
form_with
については
form_with
はform_tag
の機能とform_for
の機能を1つで使い分け
form_tag
動作
form_tag
はフォームブロックの各要素の値を直接ハッシュで取り出すため、モデルは介在しない。
form_tag
ではアクションへのパスを指定し、アクションで各要素の値を参照する- 各要素の値は1次元のハッシュ
params
に格納されている - 要素の値は要素の
name
属性をキーとして参照
コード間の流れ
app/views/top.html.erb
form_tag
ヘルパーの引数に、POSTを送るアクションへのパスを指定- パス指定にはroutes.rbで設定するエイリアスを使っている
- 各要素ヘルパーの第1引数で
id/name
属性の値をシンボルで定義
1 2 3 4 5 |
<%= form_tag(from_form_tag_path) do %> <%= text_field_tag(:param_text, nil, placeholder: "テキスト入力") %> <%= password_field_tag(:param_pass, nil, placeholder: "パスワード入力") %> <%= submit_tag("送信") %> <% end %> |
config/routes.rb
- form_tagからのPOSTを受け取るアクションへのルートを設定
- URLにエイリアスを使っていて、それをアクションに紐づけている
1 2 3 4 |
Rails.application.routes.draw do root to: 'pages#top', as: :top post 'from_form_tag', to: 'pages#form_tag_process', as: :from_form_tag end |
app/controllers/pages_controller.rb
- ルーティングの結果呼ばれたアクション内で、フォームの要素から送られた値を処理
- 要素の値は
params[キーのシンボル]
で取り出す
1 2 3 4 5 6 7 8 9 10 11 12 |
class PagesController < ApplicationController def top end def form_tag_process puts "----- form_params -----" p params p params[:param_text] p params[:param_pass] redirect_to top_path and return end end |
コンソール出力
- 4行目でPOSTで送られる
params
全体が表示されている - 6行目で
params
を表示させており、4行目と同じ内容 - 7~8行目は個別の要素の値をキーを使って参照
1 2 3 4 5 6 7 8 9 10 |
Started POST "/from_form_tag" for 10.0.2.2 at 2021-03-10 23:33:14 +0900 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by PagesController#form_tag_process as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"r2MLrmxMn/q+ox6XCSu6snB8SMU7qDF9ePvsLcYdNmkVTDt6HQuVnO3ZLRvQXCbhR9TPBIkSHnZzK/841Xf9Bw==", "param_text"=>"TEXT", "param_pass"=>"PASSWORD", "commit"=>"送信"} ----- form_params ----- <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"r2MLrmxMn/q+ox6XCSu6snB8SMU7qDF9ePvsLcYdNmkVTDt6HQuVnO3ZLRvQXCbhR9TPBIkSHnZzK/841Xf9Bw==", "param_text"=>"TEXT", "param_pass"=>"PASSWORD", "commit"=>"送信", "controller"=>"pages", "action"=>"form_tag_process"} permitted: false> "TEXT" "PASSWORD" Redirected to http://localhost:3000/ Completed 302 Found in 1ms (ActiveRecord: 0.0ms) |
form_for
動作
form_for
では第1引数にモデルのインスタンスを指定- モデルのインスタンスはコントローラーでインスタンス変数として準備
form_for
でPOSTされた各要素の値はハッシュparams[モデル名のシンボル]
に格納される- 要素の値を取り出すには以下のハッシュを参照
params[モデル名のシンボル][要素名のシンボル]
- ここでは第2引数のURLでPOST先のアクションを指定している
コード間の流れ
より具体的な実装については、モデルによるフォームデータの取得を参照。
app/views/top.html.erb
form_for
の第1引数にモデルのインスタンスを指定- インスタンスはコントローラーで生成
- 第2に引数の
url:
で指定したパスにPOST
- form_forの構文は以下のとおり
form_for(モデル, オプション群) do |フォームビルダー|
- 各要素の値は
text_field
などフォームビルダーのメソッドで要素名を指定して取得する
1 2 3 4 5 |
<%= form_for(@record, url: from_form_for_path) do |form| %> <%= form.text_field(:param_text, placeholder: "テキストを入力") %> <%= form.password_field(:param_pass, placeholder: "パスワードを入力") %> <%= form.submit("送信") %> <% end %> |
config/routes.rb
form_tag
からのPOSTを受け取るアクションへのルートを設定- ここでは
url:
で指定したエイリアスからアクションに紐づけている
1 2 3 4 |
Rails.application.routes.draw do root to: 'pages#top', as: :top post 'from_form_for', to: 'pages#form_for_process', as: :from_form_for end |
app/controllers/pages_controller.rb
- ルーティングの結果呼ばれたアクション内で、フォームの要素から送られたデータを確認
- 各要素の値は
params[モデル名のシンボル]
にハッシュとしてまとめられている - 個別の要素の値はその中で要素名をキーとしてとりだせる
params[モデル名のシンボル][要素名のシンボル]
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class PagesController < ApplicationController def top @record = Record.new end def form_for_process puts "----- form_params -----" p params[:record] p params[:record][:param_text] p params[:record][:param_pass] redirect_to top_path and return end end |
なお上の例ではform_for
の引数に与えるモデルインスタンス(@user
)を最初に生成しているが、コントローラーの中ではこれを使わず、ローカルな変数(user
)を使っている。
コンソール出力
- 4行目中、
"record" => {"param_text" => "TEXT", ...}
が各要素のデータ - 6行目に全要素のハッシュ、7~8行目に個別の要素の値が表示されている
1 2 3 4 5 6 7 8 9 10 |
Started POST "/from_form_for" for 10.0.2.2 at 2021-03-10 23:22:22 +0900 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by PagesController#form_for_process as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"kxRom4t1qFU20HznmSM8ydT7b9rqD9c9GNclGgakhBopO1hP+jKiM2WqT2tAVKCa41PoG1i1+DYTBzYPFc5PdA==", "record"=>{"param_text"=>"TEXT", "param_pass"=>"PASSWORD"}, "commit"=>"送信"} ----- form_params ----- <ActionController::Parameters {"param_text"=>"TEXT", "param_pass"=>"PASSWORD"} permitted: false> "TEXT" "PASSWORD" Redirected to http://localhost:3000/ Completed 302 Found in 5ms (ActiveRecord: 0.0ms) |
form_with (データベースなし)
動作
- 動作の内容は
form_tag
と同じ - そのためビューにおいて
form_with
の引数としてURLを指定(url: パス
)
注意点
form_with
はデフォルトでajaxによるフォーム送信となり、ページ全体がレンダリングされない。HTMLとして送信してレンダリングするには、パラメーターにlocal: true
を指定する。
<%= form_with(url: 'URL', local: true) do |f| %>
コード間の流れ
- ビューでの書き方以外は
form_tag
と同じ流れ
app/views/top.html.erb
form_with
の引数にURLを指定(url: パス
)- モデルが介在しない
form_tag
と同じ動作をさせる書き方 - 各要素はフォームビルダーのメソッドで書いている
1 2 3 4 5 |
<%= form_with(url: from_form_with_non_db_path) do |form| %> <%= form.text_field(:param_text, placeholder: "テキスト入力") %> <%= form.password_field(:param_pass, placeholder: "パスワード入力") %> <%= form.submit("送信") %> <% end %> |
config/routes.rb
1 2 3 4 |
Rails.application.routes.draw do root to: 'pages#top', as: :top post 'from_form_with_non_db', to: 'pages#form_with_non_db_process', as: :from_form_with_non_db end |
app/controllers/pages_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 |
class PagesController < ApplicationController def top end def form_with_non_db_process puts "----- form_with (non DB) -----" p params p params[:param_text] p params[:param_pass] redirect_to top_path and return end end |
コンソール出力
1 2 3 4 5 6 7 8 9 10 |
Started POST "/from_form_with_non_db" for 10.0.2.2 at 2021-03-10 23:44:49 +0900 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by PagesController#form_with_non_db_process as JS Parameters: {"utf8"=>"✓", "authenticity_token"=>"lwT+bvSyq9SN/mYrripqw1w5BTs7j41fJAa391FIyHstK866hfWhst6EVad3XfaQa5GC+ok1olQv1qTiQiIDFQ==", "param_text"=>"TEXT", "param_pass"=>"PASSWORD", "commit"=>"送信"} ----- form_with (non DB) ----- <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"lwT+bvSyq9SN/mYrripqw1w5BTs7j41fJAa391FIyHstK866hfWhst6EVad3XfaQa5GC+ok1olQv1qTiQiIDFQ==", "param_text"=>"TEXT", "param_pass"=>"PASSWORD", "commit"=>"送信", "controller"=>"pages", "action"=>"form_with_non_db_process"} permitted: false> "TEXT" "PASSWORD" Redirected to http://localhost:3000/ Completed 200 OK in 3ms (ActiveRecord: 0.0ms) |
form_with (データベースあり)
より具体的な実装については、モデルによるフォームデータの取得を参照。
動作
- 動作の内容は
form_for
と同じ - そのためにビューにおいて引数にデータベースと紐づいたモデルを指定(
model: モデルインスタンス
) - ここでは第2引数のURLでPOST先のアクションを指定している
<%= form_with(model: 'model', url: 'URL', local: true) do |f| %>
コード間の流れ
app/views/top.html.erb
form_with
の引数にモデルのインスタンスを指定しているmodel: モデルインスタンス名
で指定- モデルのインスタンスはコントローラーで生成
- モデルを介在した
form_with
と同じ動作をさせる書き方 - 各要素はフォームビルダーのメソッドで書いている
1 2 3 4 5 |
<%= form_with(model: @record, url: from_form_with_db_path) do |form| %> <%= form.text_field(:param_text, placeholder: "テキストを入力") %> <%= form.password_field(:param_pass, placeholder: "パスワードを入力") %> <%= form.submit("送信") %> <% end %> |
config/routes.rb
1 2 3 4 |
Rails.application.routes.draw do root to: 'pages#top', as: :top post 'from_form_with_db', to: 'pages#form_with_db_process', as: :from_form_with_db end |
app/controllers/pages_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class PagesController < ApplicationController def top @record = Record.new end def form_with_db_process puts "----- form_with (DB) -----" p params[:record] p params[:record][:param_text] p params[:record][:param_pass] redirect_to top_path and return end end |
コンソール出力
1 2 3 4 5 6 7 8 9 10 |
Started POST "/from_form_with_db" for 10.0.2.2 at 2021-03-10 23:51:21 +0900 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by PagesController#form_with_db_process as JS Parameters: {"utf8"=>"✓", "authenticity_token"=>"JsiSAIuM5phTduwLwkuv5x05G9KyYbHNkeX1WJ5lUYmc56LU+svs/gAM34cbPDO0KpGcEwDbnsaaNeZNjQ+a5w==", "record"=>{"param_text"=>"TEXT", "param_pass"=>"PASSWORD"}, "commit"=>"送信"} ----- form_with (non DB) ----- <ActionController::Parameters {"param_text"=>"TEXT", "param_pass"=>"PASSWORD"} permitted: false> "TEXT" "PASSWORD" Redirected to http://localhost:3000/ Completed 200 OK in 1ms (ActiveRecord: 0.0ms) |