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

# はじめに

``form_tag`` と ``form_for`` は Rails5.1で soft deprecated (非推奨) となり、
将来のRailsリリースで ``form_with`` に完全に置き換えられる予定です。
そこで、この記事では、``form_tag``, ``form_for`` と ``form_with`` の違いについて触れていきたいと思います。


# 一人二役の構文

これまで、フォームを作りたいときで、関連するモデルがなかったときは ``form_tag`` を使ってきました。

```erb
<%= form_tag users_path do %>
  <%= text_field_tag :email %>
  <%= submit_tag %>
<% end %>
```

そして、モデルがあったときは ``form_for`` を使ってきました。

```erb
<%= form_for @user do |form| %>
  <%= form.text_field :email %>
  <%= form.submit %>
<% end %>
```

見ての通り、``form_for`` ではフォームのビルドヘルパー(``form.hoge``)を使うのに対し、
 ``form_tag`` では使いません。そのため、二つのフォームで構文が違っていました。

しかし、新しい``form_with`` は違います。どちらの場合でもビルドヘルパーを使います。

関連するモデルが**ない** ``form_with`` :

```erb
<%= form_with url: users_path do |form| %>
  <%= form.text_field :email %>
  <%= form.submit %>
<% end %>
```

関連するモデルが**ある** ``form_with`` :

```erb
<%= 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:

```erb
<%= form_for @user do |form| %>
  <%= form.text_field :email %>
  <%= check_box_tag :send_welcome_email %> ← ここ
  <%= form.submit %>
<% end %>
```

After:

```erb
<%= 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``スコープに設定されます。

```ruby
# コントローラにて
params[:user][:send_welcome_email]
```

したがって、場合によっては ``form.check_box`` ではなく、``check_box_tag`` を今後も使うことがあると思います。


# formタグのidとclass属性はhtml: { }で囲まなくても大丈夫

Before:

```erb
<%= form_for @user, html: { id: :custom_id, class: :custom_class } do |form| %>
<% end %>
```

After:

```erb
<%= 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のレスポンステンプレートを作れば](https://qiita.com/hc_isobe/items/a29860d3e86a6bc5fd3e)、ページ全体のリフレッシュを無くすことができるので、これを機にフォームでもXHRを使い始めてはいかがでしょうか。

もし、リモートフォームを無効にした場合は ``local: true`` と指定します。

```erb
<%= 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属性を自動付与するようになったので、すべて自分で指定する必要はなくなりました。


```erb
<%= 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`` はもう使わなくよいでしょう。(どうせ、いずれ消されますし。)

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

もっと深掘りをしたい方は、[form_withのドキュメント(英語)](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with) をどうぞ。


# 参考
[Rails 5.1's form_with vs. form_tag vs. form_for – Patrik on Rails](https://m.patrikonrails.com/rails-5-1s-form-with-vs-old-form-helpers-3a5f72a8c78a)