はじめに
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(置き換え前)
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
(ブラウザ上に表示される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 model: @user, url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>
(ブラウザ上に表示される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モデルでの実装として考えているので、@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オプション
<%= form_for(resource, as: 'hoge', url: registration_path(resource_name)) do |f| %>
(ブラウザ上に表示される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を使いましょう。
<%= form_with model: @user, scope: 'hoge', url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>
(ブラウザ上に表示される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」がありますね。
フォーム自体の(1行目の<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