- 開発
READ MORE
こんにちは、hachi8833です。Rails 6.1の#form_with
のAPIドキュメント変更を反映しました。
なお、5.1より前のform_for
やform_tag
はその後非推奨になりました。5.1以降はこのform_with
だけを使いましょう。
参考: Provide form_with as a new alternative to form_for/form_tag · Issue #25197 · rails/rails
概要
MITライセンスに基づいて翻訳・公開いたします。
ActionView::Helpers::FormHelper#form_with
ActionView::Helpers::FormHelper#form_with
ActionView::Helpers::FormHelper#form_with
(本記事執筆時点では、edgeでない6.1 APIドキュメントへの反映は一部にとどまっています)原文の更新や訳文の誤りにお気づきの方は、@hachi8833までお知らせください。
#form_with
APIドキュメント(翻訳)# API呼び出し
form_with(model: nil, scope: nil, url: nil, format: nil, **options)
URL、スコープ、モデルの組み合わせを元にformタグを作成します。
<%= form_with url: posts_path do |form| %>
<%= form.text_field :title %>
<% end %>
# =>
<form action="/posts" method="post" data-remote="true">
<input type="text" name="title">
</form>
<%= form_with scope: :post, url: posts_path do |form| %>
<%= form.text_field :title %>
<% end %>
# =>
<form action="/posts" method="post" data-remote="true">
<input type="text" name="post[title]">
</form>
<%= form_with model: Post.new do |form| %>
<%= form.text_field :title %>
<% end %>
# =>
<form action="/posts" method="post" data-remote="true">
<input type="text" name="post[title]">
</form>
<%= form_with model: Post.first do |form| %>
<%= form.text_field :title %>
<% end %>
# =>
<form action="/posts/1" method="post" data-remote="true">
<input type="hidden" name="_method" value="patch">
<input type="text" name="post[title]" value="<postのtitle>">
</form>
<%= form_with model: Cat.new do |form| %>
<%= form.text_field :cats_dont_have_gills %>
<%= form.text_field :but_in_forms_they_can %>
<% end %>
# =>
<form action="/cats" method="post" data-remote="true">
<input type="text" name="cat[cats_dont_have_gills]">
<input type="text" name="cat[but_in_forms_they_can]">
</form>
フォームのパラメータは、コントローラでパラメータのネストに沿ってアクセスできます。つまり、inputフィールドにtitle
とpost[title]
というフィールド名がある場合、コントローラではそれぞれparams[:title]
とparams[:post][:title]
としてアクセスできます。
フォームのinputフィールド名 | コントローラのparams |
---|---|
title |
params[:title] |
post[title] |
params[:post][:title] |
更新(2021/01/22)
(6.1 edge)このパラグラフはRails 5.1〜6.0まで存在しましたが、6.1で削除されました。詳しくは『Rails 6.1で form_withのデフォルトが「remoteなし」になった(翻訳)』をどうぞ。
![]()
(6.1 edge)上述のコード例では、比較しやすさのため送信ボタンを省略しています。また、UTF-8サポートを有効にする自動生成のhidden
フィールドや、CSRF(Cross Site Request Forgery)保護に必要な認証トークンも省略しています。
上述のコード例の多くでは、単にform_with
に:model
を渡しています。これはRESTfulなルーティングのセットに対応しており、そのほとんどはconfig/routes.rbのresources
で定義されます。
したがって、そうしたモデルのレコードを1件渡せば、RailsがURLやメソッドを推測します。
<%= form_with model: @post do |form| %>
...
<% end %>
上のコードは以下のようなコードと同等になります。
<%= form_with scope: :post, url: post_path(@post), method: :patch do |form| %>
...
<% end %>
新規レコードについても同様です。
<%= form_with model: Post.new do |form| %>
...
<% end %>
上のコードは以下のようなコードと同等になります。
<%= form_with scope: :post, url: posts_path do |form| %>
...
<% end %>
#form_with
で利用できるオプション:url
url_for
やlink_to
で渡せる値と似ています。たとえば、名前付きルートを直接渡すこともできますし、:url
なしで:scope
を渡すと、現在のURLにフォームを送信することもできます。:method
:get
や:post
を指定します。:patch
、:put
、:delete
を指定すると、隠しinput名の後ろに_method
が追加され、POST verb上でこれらのHTTP verbをシミュレートします。:format
:json
など通常と異なるリソースタイプを送信するのに便利です。:url
がオプションに渡されている場合、このオプションはスキップされます。:scope
:namespace
namespace
属性で指定した名前にアンダースコアを追加したものが、生成されたHTML idの前に追加されます。:model
:url
や:scope
の自動推測に使うモデルオブジェクトを指定し、inputフィールドにモデルの値を表示します。title
属性の値が"Ahoy!"
ならtitle
の入力フィールドの値に"Ahoy"
と表示されます。:scope
か:url
を渡します(params[:post]
をparams[:article]
に変更するなど)。:authenticity_token
false
を渡して認証トークンのフィールドをスキップすることもできます。config.action_view.embed_authenticity_token_in_remote_forms = false
を指定すると、埋め込み認証トークンがremoteフォームで省略されることがあります。この指定はフォームでフラグメントキャッシュを使う場合に便利です(remoteフォームがmeta
タグから認証トークンを取得するようになるので、JavaScriptがオフになっているブラウザをサポートする場合を除けば認証トークンをフォームに埋め込む必要がなくなります)。:local
local: false
を指定するとremoteの「控えめな(unobtrusive)」XHR送信が有効になります。Railsの設定でconfig.action_view.form_with_generates_remote_forms = true
を設定すると、remoteのXHR送信をデフォルトで有効にできます。:skip_enforcing_utf8
true
を指定すると、送信時にutf8
という名前の隠しフィールドが出力されなくなります。:builder
:id
id
属性を指定します(オプション)。:class
class
属性を指定します(オプション)。:data
data
属性を指定します(オプション)。:html
#form_with
にブロックを渡さない場合は、開始formタグを生成します。
# 名前付きパスを指定する場合
<%= form_with(model: @post, url: super_posts_path) %>
# スコープを追加する場合
<%= form_with(model: @post, scope: :article) %>
# フォーマットを指定する場合
<%= form_with(model: @post, format: :json) %>
# トークンを無効にする場合
<%= form_with(model: @post, authenticity_token: false) %>
ルーティングをadmin_post_url
のように名前空間化する場合は以下のようにします。
<%= form_with(model: [ :admin, @post ]) do |form| %>
...
<% end %>
たとえばリソースに関連付けが定義されているとします。ルーティングが正しく設定されているdocumentにcommentを追加したい場合は次のようにします。
<%= form_with(model: [ @document, Comment.new ]) do |form| %>
...
<% end %>
上のdocumentには@document = Document.find(params[:id])
が既に与えられているとします。
#form_with
ではFormBuilderオブジェクトが使われていますが、単独のFormHelperのメソッドやFormTagHelperのメソッドと共存させることもできます。
<%= form_with scope: :person do |form| %>
<%= form.text_field :first_name %>
<%= form.text_field :last_name %>
<%= text_area :person, :biography %>
<%= check_box_tag "person[admin]", "1", @person.company.admin? %>
<%= form.submit %>
<% end %>
同様に、FormOptionsHelperのメソッド(FormOptionsHelper#collection_select
など)と共存させたり、DateHelperのメソッド(ActionView::Helpers::DateHelper#datetime_select
など)と共存させることもできます。
以下のHTTP verbの完全な配列をoptions
ハッシュに渡すことができます。
method: (:get|:post|:patch|:put|:delete)
verbがGET
やPOST
以外の場合(この2つはHTMLフォームでネイティブでサポートされます)、フォームそのものにはPOST
verbが設定され、_method
という名前の隠しinputフィールドには指定の verbが設定され、後者がサーバーで解釈されます。
HTMLのdata-*
属性はdata:
ハッシュで直接渡せますが、id:
やclass:
を含む他のすべてのHTMLオプションについては次のようにhtml:
ハッシュの中に置く必要があります。
<%= form_with(model: @post,
data: { behavior: "autosave" },
html: { name: "go" }) do |form| %>
...
<% end %>
上のコードから以下のHTMLが生成されます。
<form action="/posts/123" method="post" data-behavior="autosave" name="go">
<input name="_method" type="hidden" value="patch" />
...
</form>
#form_with
メソッドを使うと、自動的にモデルidが隠しフィールドとしてフォームに含まれます。このモデルidは、フォームデータとそれに関連付けられているモデルとの関連を保つために使われます。
ORMシステムによってはネストしたモデルでこうしたidを使わないものもあるので、その場合は次のようにinclude_id: false
を指定することで隠しフィールドのモデルidを出力しないようにできます。
<%= form_with(model: @post) do |form| %>
<%= form.fields(:comments, skip_id: true) do |fields| %>
...
<% end %>
<% end %>
上の例では、NoSQLデータベースにPost
というモデルがひとつと、それに一対多で関連付けられるComment
というモデルが保存されています。:comments
には主キーはありません。
FormBuilderクラスをカスタマイズしてフォームをビルドすることもできます。カスタマイズするには、FormBuilderを継承してサブクラスを作り、必要なヘルパーメソッドを定義またはオーバーライドします。
次のコード例では、フォームのinputにラベルを自動追加するヘルパーを作成済みであることが前提です。
<%= form_with model: @person, url: { action: "create" }, builder: LabellingFormBuilder do |form| %>
<%= form.text_field :first_name %>
<%= form.text_field :last_name %>
<%= form.text_area :biography %>
<%= form.check_box :admin %>
<%= form.submit %>
<% end %>
上のようにコードを書いてから、次のコードを書きます。
<%= render form %>
これにより、people/_labelling_form
というテンプレートを使ってレンダリング(=HTML生成)され、フォームビルダを参照するローカル変数の名前はlabelling_form
になります。
特に指定しない限り、カスタムのFormBuilder
クラスは、ネストした#fields_for
呼び出しのオプションと自動的にマージされます。
上のようなコードを別のヘルパーにも含めておきたい場合は、以下のように書くこともできます。
def labelled_form_with(**options, &block)
form_with(**options.merge(builder: LabellingFormBuilder), &block)
end