form_withとは
form_withとは、railsで情報を送信するためのヘルパーメソッド。 form_withを使うことにより、簡単に入力フォームに必要なHTMLを作成することができます。
特定のコントローラーで任意のデータを受け取りたい or 受け取ったデータを永続化しない
- 特定の画面のフォームから送られてきた値を任意のコントローラーで利用したい場合
- または関連するモデルが存在しない = 送られてきた値を特定のモデルに永続化しない場合
これらの場合は、以下のようにurlオプションからコントローラーを指定します。 必要に応じてmethodオプションからHTTPメソッド(=アクション)を指定することも可能です。
<%= form_with url: users_path, method: :get do |form| %> <!--フォーム内容 --> <% end %>
生成されるHTML
<form action=”/users” method=”get”> ・・・ </form>
送られてきたデータをDBに保存して永続化したい
- 関連するモデルが存在する = フォームから送られてきたparamsをDBに保存したい場合
この場合は、保存したい対象のモデルをmodelオプションに指定します。 methodオプションは指定がなかった場合POSTがデフォルトのHTTPメソッドとして選択されます。
<%= form_with model: User.new do |form| %> <%= form.text_field :name %> <%= form.number_field :age %> <%= form.submit %> <% end %>
生成されるHTML
<form action=”/users” method=”post”> <input type="text" name="user[name]" id="user[name]"> <input type="number" name="user[age]" id="user[age]"> <input type="submit" name="commit" value="保存する" data-disable-with="保存する"> </form>
渡ってくるparams
params => <ActionController::Parameters { "utf8"=>"✓", "authenticity_token"=>"<token>", "user"=>{"name"=>"garasi", "age"=>"29"}, "commit"=>"送信", "controller"=>"users", "action"=>"create" } permitted: false>
modelオプションが指定されている場合のデフォルトの挙動について
- 該当のモデルオブジェクトから特定したcontroller・アクションに対するPOSTリクエストの送信
- 渡されたインスタンスが空ならPOST(create), IDを持っていればPATCH(update)がHTTPメソッドとして指定される(methodオプションで任意のHTTPメソッドを指定することも可能)
- ActiveRecordと連携してモデル名とモデルの属性名を、htmlのinput要素にあるname属性として付与する
ネストしたモデルに対してデータを永続化したい
子要素のモデルに対してデータを永続化する場合の記述の仕方が若干変わります。 ある記事に対してユーザーがコメントを投稿するフォームを例にあげて考えてみましょう。 まずはコントローラーにてインタンス変数の定義を行います。
def new @article = Article.find(params[:article_id]) @comment = Comment.new end def edit @article = Article.find(params[:article_id]) @comment = Comment.find(params[:id]) end
コメントは必ずいずれかの記事に紐づいているので、どの記事に対するコメントなのかという情報が必要となります。 そのため@article = Article.find(params[:article_id])でコメントが行われた記事を特定します。 ビュー側では下記のようなイメージで、引数に配列を渡す渡すことでネストされた子要素に対するリクエストの送信が可能となります。
<%= form_with model: [@article, @comment] do |form| %> <%= form.text_field :text %> <%= form.submit %> <% end %>
Rails 5.1以前のformヘルパー
form_tag
- 関連するモデルが存在しない = 送られてきた値を特定のモデルに永続化しない場合
form_withでいうところのurlオプションを使用する際の要件に一致する場合、5.1以前ではform_tagを用いてこれを実現していました。 使い方としては第一引数に送信先のURL、第二引数にリクエスト時のHTTPメソッドを指定する形です。
<%= form_tag('/main', method: :post) do %> <input type="text" name="nickname"> <input type="submit"> <% end %>
form_for
- 関連するモデルが存在する = フォームから送られてきたparamsをDBに保存したい場合
form_withでいうところのmodelオプションを使用する際の要件に一致する場合、5.1以前ではform_forを用いてこれを実現していました。 使い方としては引数としてモデルのインスタンスを渡すのみ。form_forではFormBuilderオブジェクトのヘルパーメソッドを使用できるため、フィールドの属性とインスタンスのプロパティを適宜指定します。
<%= form_for(@user) do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %>
まとめ
form_withについて
関連するモデルが存在しない = 送られてきた値を特定のモデルに永続化しない場合は、urlオプションでコントローラーとアクションを直指定してリクエストを飛ばす
関連するモデルが存在する = フォームから送られてきたparamsをDBに保存したい場合は、modelオプションに任意のモデルのインスタンスを渡して、ORマッピングに沿ったデータの永続化を行う
ネストした子要素のモデルに対してはmodelオプションに配列を渡すことでリクエストできる
form_withと旧form_for, form_tagとの関係
modelとurlの指定により用途の使い分けを可能にし、書き方を統一したのがform_with
Rails5.1以降のアプリケーションでは基本的にform_withで書くのが一般的
昔のプロダクトでform_tag, form_forを利用している箇所があった場合に、出力されるhtmlがイメージできる程度にはなっておく必要がある