Rails
form
RubyOnRails
Rails5
form_with

【Rails 5】(新) form_with と (旧) form_tag, form_for の違い


はじめに

form_tagform_for は Rails5.1で soft deprecated (非推奨) となり、

将来のRailsリリースで form_with に完全に置き換えられる予定です。

そこで、この記事では、form_tag, form_forform_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_emailuserスコープに設定されます。

# コントローラにて

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_tagform_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_tagform_for はもう使わなくよいでしょう。(どうせ、いづれ消されますし。)

もしこの記事がみなさんの役に立てば幸いです。:muscle:

もっと深掘りをしたい方は、form_withのドキュメント(英語) をどうぞ。


参考

Rails 5.1's form_with vs. form_tag vs. form_for – Patrik on Rails