【Rails】form_withの使い方

form_withの使い方について復習。と、理解を深める。

form_tagとform_for

たまに見かけるform_withによく似た形のフォームヘルパーですが、これらは少し古いもので、今ではform_withがこれら2つの役割をどっちも担ってくれているため、基本的にはform_tagとform_forは使わない。 簡単に説明しておくと、
form_tag は関連するモデルが無く、入力されたデータを保存しない場合に使用。 form_for は関連するモデルがあって、入力されたデータを保存する場合に使用。

form_with

モデルの有無に関わらずform_withで入力フォームを作成できる。

  • 関連モデルが無いform_withの記述(urlを指定)
<%= form_with url: users_path do |form| %>
  <%= form.text_field :nickname %>
  <%= form.submit %>
<% end %>
  • 関連モデルがあるform_withの記述(modelを指定)
<%= form_with model: @user do |form| %>
  <%= form.text_field :nickname %>
  <%= form.submit %>
<% end %>

form_withの引数にモデルクラスのインスタンスを指定すると、自動的にどのURLに飛ぶかを判断してくれる。ここで言うと、ユーザー情報が無ければこれから作るんだなと思ってcreateアクションに行き、ユーザー情報が既に存在していれば更新するんだなと思ってupdateアクションに行くようにしてくれる。(引数となるインスタンス変数を以下のように定義しているため)

def new
  @user = User.new
end

def edit
  @user = User.find(params[:id])
end

FormBuilderオブジェクト

form_withの引数にある@userの情報を持っている「form」のこと。(もしくは「f」)
これによって、text_fieldやsubmitといったFormBuilderオブジェクトのヘルパーメソッドを使うことができる。
各formで取得した情報ををブロック変数の中のformに集めてコントローラーに持っていくというイメージかな。

ネストしているとき

例えば、Tweetに対するコメントのように、ルーティングでネスト構造になっているときは、以下のように引数に配列で変数を渡す記述をする。

<%= form_with model: [@tweet, @comment] do |form| %>
  <%= form.text_field :text %>
  <%= form.submit %>
<% end %>

このときのコントローラーでは、@tweetと@commentの両方を定義しておく。

def new
  @tweet = Tweet.find(params[:tweet_id])
  @comment = Comment.new
end

def edit
  @tweet = Tweet.find(params[:tweet_id])
  @comment = Comment.find(params[:id])
end

methodオプション

送信するHTTPメソッドを指定する。デフォルトはPOSTなので省略することが多い。
データ削除のときなどに以下のように記述する

<%= form_with model: @tweet, method: :delete do |form| %>
  <%= form.text_field :text %>
  <%= form.submit %>
<% end %>

localオプション

送信方法を指定する。デフォルトではリモートフォームになっていて、非同期通信のXHR(Ajax)リクエストで送信される。
XHRが何の略か分からない人は、local: true を指定して、リモートフォームを無効にしておく。

終わりに

form_withの歴史をほんのちょっと知ることができました。form_withの記述がややこしく感じた理由もわかりました。モデルの状況や通信方法などを意識しながら、正しい記述ができるようにしていきたいと思います。