はじめに
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