form_withの:scopeオプション

f:id:endoakak:20200727195635j:plain

いつも当たり前のようにお世話になっているform_withなのですが、時々登場する:scopeって何なんだろうとずっと気になりつつもスルーしていたので、ちょっと考えてみました。

form_withの書き方

まずはform_withの書き方。モデルのインスタンスを与える場合とURLを指定する場合があります。

<%= form_with model: @post, local: true do |f| %>
  <%= f.label :title, "title" %>
  <%= f.text_field :title %>
  <%= f.submit "submit" %>
<% end %>
<%= form_with url: posts_path, local: true do |f| %>
  <%= f.label :title, "title" %>
  <%= f.text_field :title %>
  <%= f.submit "submit" %>
<% end %>

送った値をデータベースに保存するときはモデルのインスタンスを与え、保存する必要がないときはURLの指定でいい、というように習った気がします。

scopeの使い方

:scopeは各inputのname属性をグループ化するのに使います。

以下の例の、for、name、idあたりに注目してみてください。

まずはscopeなしのフォームです。

<%= form_with url: posts_path, local: true do |f| %>
  <%= f.label :title, "title" %>
  <%= f.text_field :title %>
  <%= f.submit "submit" %>
<% end %>


これから生成されるHTMLは次のようになります。

<form action="/posts" accept-charset="UTF-8" method="post">
  <input type="hidden" name="authenticity_token" value="lZncvnvoVbrMXAFZh+OKMm0tffHWRpHobLG8u8rfbyGV0IvY0+bReGM7s4gsx6g7EhDHQyJGnqAKYcFR0qNv9w==">
  <label for="title">title</label>
  <input type="text" name="title" id="title">
  <input type="submit" name="commit" value="submit" data-disable-with="submit">
</form>


これにscopeを加えます。

<%= form_with url: posts_path, scope: :post, local: true do |f| %>
  <%= f.label :title, "title" %>
  <%= f.text_field :title %>
  <%= f.submit "submit" %>
<% end %>


生成されるHTMLはこうなります。

<form action="/posts" accept-charset="UTF-8" method="post">
  <input type="hidden" name="authenticity_token" value="6tBiAMKg7985ctmGN7VNykTOPyt0XQv9mngAmaIcjjLqmTVmaq5rHZYVa1eckW/DO/OFmYBdBLX8qH1zumCO5A==">
  <label for="post_title">title</label>
  <input type="text" name="post[title]" id="post_title">
  <input type="submit" name="commit" value="submit" data-disable-with="submit">
</form>


labelのfor属性、inputのid属性がtitleからpost_titleに変わり、name属性がtitleからpost[title]に変わりました。

これにより、リクエストにのせて送られるパラメーターの構造が変わります。

scopeを設定していない場合はparams[:title]でtitleを取得でき、scope: postを設定した場合はparams[:post][:title]でtitleを取得できます。

paramsの中のpostというハッシュの中のtitle、という感じですね。

ちなみにモデルのインスタンスを与えた場合は、scopeを設定しなくても初めからグループ化されています。

まとめ

  • form_withの書き方はモデルを指定する方法とURLを指定する方法の二通り
  • scopeを設定することで、フォームの各要素のname属性をグループ化できる
  • パラメーターの階層が一つ深くなる