【Rails】deviseのビューのform_forをform_withに置き換える

# はじめに

deviseが元々保証してるビューを使ったとき、ユーザー登録のためのヘルパーメソッドは「form_for」によって実装されています。(devise(4.6.1)、2019年3月12日時点)

「今後はform_withに変えていこうぜ」というお達しも出ていることですし、Rails5.1以降のアプリでdeviseが自動生成したビューをそのまま使っている場合は、自分で変更することも検討していいかもしれません。

今回は、app/views/devise/registrations配下の「new.html.erb」について、「form_for」から「form_with」に置き換えてみます。

# 置き換える

## form_for(置き換え前)


```erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
```

<br>
(ブラウザ上に表示されるhtmlソースコード)

```html
<form class="new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓">

  <!-- ... -->

</form>
```


## form_with(置き換え後)

```erb
<%= form_with model: @user, url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>
```

<br>
(ブラウザ上に表示されるhtmlソースコード)

```html
<form class="new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓">

  <!-- ... -->

</form>
```

これでform_withでの置き換えは完了です。
書き換えたオプションの詳細を確認しましょう。

# form_withのオプションについて

## local: true
自動で行われるAjax通信を無効化するためのオプションです。
このオプションによって、\<form>の中に「data-remote="true"」という記述がされることを防いでいます。
このオプションがないとJavaScriptによってレンダリングし、リダイレクト処理が行われないため、画面が遷移しなくなります。

## model: @user 
ここはモデル名によって変わってきますが、今回はUserモデルでの実装として考えているので、@&#xFFF0;user（User.new)を指定します。
form_for(置き換え前)でのresourceに相当します。

## url: user_registration_path

form_forでのurl: registration_path(resource_name)に当たりますが、より明示的になるよう記述を変更しました。
このオプションはhtml上での「action="/users"」に当たります。


## id: 'new_user' と class: 'new_user'

html上での「class="new_user" id="new_user"」に当たる箇所です。
form_withでは自動で付与されないようなので、明示的に記入します。


## as: resourceに当たる所がform_withで書き換えたビューにないけどどこいった？

これは各inputフィールドのname属性を明示的にグループ化するためによく使われる記述ですが、
form_forでもform_withでも、モデル名（リソース名）で自動補完的にグループ化されるようなので、今回の場合は対応するものをform_withで準備しなくても問題はありません。

ただ、form_forでの:asオプションの挙動と、form_withの場合ではどのオプションを使えばいいかということについては、確認しておきましょう。


## 挙動の確認

### form_forでの:asオプション

```erb
<%= form_for(resource, as: 'hoge', url: registration_path(resource_name)) do |f| %>
```

<br>
(ブラウザ上に表示されるhtmlソースコード)

```html
<form class="new_hoge" id="new_hoge" action="/users" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓">
  <input type="hidden" name="authenticity_token" value="SgDrx+kPXXBTULNk6U8Au4SxYUaVMs6eFTHW+O80UT4KFQnp7hRV+u6F0TsTen44yjGNdd/xWKikYtSNT1uPrQ==">
  

  <div class="field">
    <label for="hoge_email">Email</label><br>
    <input autofocus="autofocus" autocomplete="email" type="email" value="" name="hoge[email]" id="hoge_email">
  </div>

   <!-- ... -->

</form>
```
\<form>のid、classと、laberのfor属性と、inputフィールドのname属性とidに「hoge」が反映されて表示されます。

### form_withでの:scopeオプション

form_withでは:asオプションは機能しないので:scopeを使いましょう。

```erb
<%= form_with model: @user, scope: 'hoge', url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>
```
<br>
(ブラウザ上に表示されるhtmlソースコード)

```html

<form id="new_user" class="new_user" action="/users" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓">
  <input type="hidden" name="authenticity_token" value="1TJ87nrrWc02LwzHkKqPVPdLUwghnekawy5C848Yp0SVJ57AffBRR4v6bphqn/HXucu/O2tefyxyfUCGL3d51w==">
  

  <div class="field">
    <label for="hoge_email">Email</label><br>
    <input autofocus="autofocus" autocomplete="email" type="email" value="" name="hoge[email]" id="hoge_email">
  </div>

  <!-- ... -->

</form>
```

laberのfor属性とinputフィールドのname属性とidに「hoge」がありますね。
フォーム自体の(１行目の\<form>の)idとclassはオプションで明示的にしている「id: 'new_user', class: 'new_user'」に従います。
（というより、そもそも:scopeオプションはそこに関与しません。）

### :scopeオプションのおかげでinputフィールドのname属性がhogeでグループ化されている

ということはコントローラではparams[:hoge]でアクセスできます。
ストロングパラメータとしてはparams.require(:hoge).permit(:email, :fuga, :foo)といった形でアクセスしましょう。

:scopeオプションを使用しない場合は、モデル名(user)でグループ化してくれるので、params[:user]でアクセスできます。


# 参考にさせて頂いたサイト・記事

[Rails 5.1] ‘form_with’ APIドキュメント完全翻訳
https://techracho.bpsinc.jp/hachi8833/2017_05_01/39502

【Rails 5】(新) form_with と (旧) form_tag, form_for の違い
https://qiita.com/hmmrjn/items/24f3b8eade206ace17e2

「ActionView::Helpers::FormHelper」
https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with

「MDN web docs \<form>」
https://developer.mozilla.org/ja/docs/Web/HTML/Element/form