はじめに
form_tag
と form_for
は Rails5.1で soft deprecated (非推奨) となり、
将来のRailsリリースで form_with
に完全に置き換えられる予定です。
そこで、この記事では、form_tag
, form_for
と form_with
の違いについて触れていきたいと思います。
一人二役の構文
これまで、フォームを作りたいときで、関連するモデルがなかったときは form_tag
を使ってきました。
<%= form_tag users_path do %>
<%= text_field_tag :email %>
<%= submit_tag %>
<% end %>
そして、モデルがあったときは form_for
を使ってきました。
<%= form_for @user do |form| %>
<%= form.text_field :email %>
<%= form.submit %>
<% end %>
見ての通り、form_for
ではフォームのビルドヘルパー(form.hoge
)を使うのに対し、
form_tag
では使いません。そのため、二つのフォームで構文が違っていました。
しかし、新しいform_with
は違います。どちらの場合でもビルドヘルパーを使います。
関連するモデルがない form_with
:
<%= form_with url: users_path do |form| %>
<%= form.text_field :email %>
<%= form.submit %>
<% end %>
関連するモデルがある form_with
:
<%= form_with model: @user do |form| %>
<%= form.text_field :email %>
<%= form.submit %>
<% end %>
モデルを渡したときは、URLとスコープが自動推測されます。(これまでのform_for
と同じ。)
URL: @user
がDBに存在するときはupdateアクションに、ないときはcreateアクションに飛びます。
スコープ: params[:email]
が params[:user][:email]
になります。
フォームフィールドはモデルの属性に対応しなくても大丈夫
Before:
<%= form_for @user do |form| %>
<%= form.text_field :email %>
<%= check_box_tag :send_welcome_email %> ← ここ
<%= form.submit %>
<% end %>
After:
<%= form_with model: @user, local: true do |form| %>
<%= form.text_field :email %>
<%= form.check_box :send_welcome_email %> ← ここ
<%= form.submit %>
<% end %>
なお、後方の例では、send_welcome_email
はuser
スコープに設定されます。
# コントローラにて
params[:user][:send_welcome_email]
したがって、場合によっては form.check_box
ではなく、check_box_tag
を今後も使うことがあると思います。
formタグのidとclass属性はhtml: { }で囲まなくても大丈夫
Before:
<%= form_for @user, html: { id: :custom_id, class: :custom_class } do |form| %>
<% end %>
After:
<%= form_with model: @user, id: :custom_id, class: :custom_class do |form| %>
<% end %>
リモートフォームがデフォルトとなります
form_with
で生成されたフォームは、デフォルトで非同期通信のXHR(Ajax)リクエストで送信されます。
よって、form_tag
と form_for
のように、remote: true
と指定する必要がありません。
これまでも、Turbolinksのおかげで、(ほとんど)すべてのGETリクエストはデフォルトでXHRリクエストになっていましたが、今回の form_with
で、フォームでもXHRを使うようにRails公式が促しているような印象を受けました。ちなみに、JavaScriptのレスポンステンプレートを作れば、ページ全体のリフレッシュを無くすことができるので、これを機にフォームでもXHRを使い始めてはいかがでしょうか。
もし、リモートフォームを無効にした場合は local: true
と指定します。
<%= form_with model: @user, local: true %>
<% end %>
idとclass属性の自動付与について (Rails5.2の変更)
Rails 5.1のform_with
では、input要素などのフォームフィールドへのidとclass属性の自動付与が無効となっていて、すべて自分で指定する必要がありましたが、Rails 5.2以降では、form_tag
と form_for
のように、id属性を自動付与するようになったので、すべて自分で指定する必要はなくなりました。
<%= form_with User.new do |form| %>
<%= form.text_field :email %>
<% end %>
Rails 5.1:
<form action="/users">
<input type="text" name="user[email]" />
</form>
Rails 5.2:
<form class="new_user" id="new_user" action="/users">
<input type="text" name="user[email]" id="user_email" />
</form>
最後に
これからは form_with
を使うようにしましょう。
form_tag
と form_for
はもう使わなくよいでしょう。(どうせ、いずれ消されますし。)
もしこの記事がみなさんの役に立てば幸いです。
もっと深掘りをしたい方は、form_withのドキュメント(英語) をどうぞ。
参考
Rails 5.1's form_with vs. form_tag vs. form_for – Patrik on Rails