Torihaji's Growth Diary

Little by little, no hurry.

Rails7 form_withメソッド のあれこれ

はじめに

みなさん、こんにちは torihaziです。

今日は前回に引き続き自身の知識棚卸し回です。

2ヶ月前くらいの自分に説明するつもりで書いていきたいと思います。

どうぞ。

form_withについて

form_withとは Action_viewが持つヘルパーメソッドの1つです。

Action_viewとは Railsにおいてビューなどを処理するコンポーネントのことです。

ヘルパーメソッドは そのビューを作る上で便利であるメソッドのことでRailsがあらかじめ用意をしてくれています。

このfom_withメソッドを使用することによってhtmlにおける フォーム部分を生成することができます。

基本書式(いくつかある)

<%= form_with [宛先] [, オプション] do |ブロック|  %>
~~~
<% end %>

何も引数を与えずに実装すると form要素が作成されます。

私は今のところその用途に出会ったことがないので、宛先は必須としてもいいと思います。

また宛先については モデルインスタンスかルーティングヘルパーの2つのやり方を覚えておけばまず大丈夫だと思います。

ルーティングヘルパーのやり方(例)

<%= form_with url: posts_path do |f| %>
    <%= f.text_filed :title %>
<% end %>

モデルインスタンスのやり方 (例)

<%= form_with model: Post.new do |f| %>
   <%= f.text_field :title %>
<% end %>

モデルのやり方についてはコントローラで定義した内容によってものが違います。

例えばコントローラ側で@post = Post.newとした場合は 下記も可能です。

<%= form_with model: @post do |f| %>
   <%= f.text_field :title %>
<% end %>

上記で formを作成した場合、生成されるHTMLは次のようになります。

<form action="/posts" method="post">
  <input type="text" name="post[title]">
</form>

あとmodelのやり方をしたとき、与えるモデルインスタンス

どのような状態であるかによってformは変わります。

どのような状態とはどういうことかというとすでにデータが存在するかしない状態かということです。

例えば登録フォームではコントローラ側で下記のように指定するのが一般的です。

def new
    @post = Post.new
end

この時データはまだsaveメソッドを使っていないので DB上にありません。

これをnew.html.erbなどで呼び出して form_withで記載したとすると

<%= form_with model: @post do |f| %>
   <%= f.text_field :title %>
<% end %>

ちなみに更新フォームがあろうものならコントローラとビューは次のようになります。

def update
    @post = Post.find(params[:id])
end
<%= form_with model: @post do |f| %>
   <%= f.text_field :title %>
<% end %>

この時は すでにDB上にあります。

ここで登録フォームと更新フォームの書き方が同じですが、Railsは渡された@postの状態によって

登録アクションに飛ばすのか、更新アクションに飛ばすのかを勝手にやってくれます。

便利で楽ですが、何も考えずに使用するといざという時に混乱するのでよく理解しておきましょう。

値の受け取り方

このフォームでは submitボタンを生成していませんが、仮に作成してデータを送信したとします。

その時送信先のコントローラ側で name=post[title]のデータを受け取りたい場合は

params[:post][:title]で取得可能です。

formの項目について

肝心のform要素はユーザからの入力を受け付ける場所です。

そのため入力できる項目を用意する必要があります。

これまで使ったことのあるものについて説明します。

cssとしてBootstrap5のものを使用し、ブロックは fとしています。

基本書式は

f.***_field :value [,オプション]

という形です。valueの名前はなんでも良いです。

コントローラで取り出す際はparams[:value]です。

文字入力 (1行程度の)

<%= f.label :name, class: "form-label"%>
<%= f.text_field :name, class: "form-control md-3" %>

f.labelとすることで入力フォームのラベルを生成することが可能です。

ラベルとはこのフォームには何を入力すべきかを示す文のことです。

このf.label :nameであると nameを入力するフォームであるので Nameとフォームの上に表示されます。

そのためこのNameを見てユーザは Nameを入力すべきだと判断することができます。

f.text_fieldとすることで 1行程度で済む文字列の入力フォームを作成できます。

このデータを受け取る際は上の例であれば params[:name]です。

文章入力 (複数行)

<%= f.label :description, class: "form-label"%>
<%= f.text_area :description, rows: 4, class: "form-control" %>

f.text_areaとすることで 複数行にわたる文字列の入力フォームを作成できます。

rowsオプションを付与すると 4行まで入力できる高さのフォームが作成されます。

このデータを受け取る際は上の例であればparams[:description]です。

数値入力

<%= f.label :price, class: "form-label"%>
<%= f.number_field :price, min: 1, class: "form-control" %>

f.number_fieldとすることで数値の入力が可能となります。

フォームも数値の増減が可能なプルダウンのようなものがついているものが生成されます。

minオプションで入力可能な最小の値を設定することが可能です。

このデータを受け取る際は上の例であれば params[:price]です。

画像入力(ファイルも)

<%= f.label :image, class: "form-label"%>
<%= f.file_field :image, accept: "image/jpeg, image/png", class: "form-control" %>

f.file_fieldとすることでファイル入力が可能となります。

acceptオプションで入力を受け付けるファイルを指定することができます。

今回の場合であれば jpeg形式もしくはpng形式の画像ファイルを入力可能としています。

このデータを受け取る際は上の例であれば params[:image]です。

なお複数枚画像であれば

f.file_filed :image[]となり、params[:image[]]のようになります。

追記 namespace使用したとき

先の説明で form_withの宛先は モデルインスタンスかルーティングヘルパーのどちらかを覚えておけば良いと言いました。

最近他のやり方を知ったので以下に説明します。

名前空間を使っている場合、modelの書き方は次のようになります。

    <%= form_with model: [:admin, @products] do |f| %>

こうすることでadminという名前空間のproductコントローラに

フォームのデータを飛ばすことができます。

終わりに

私が今のところ触ったことあるフォームの内容はこの程度です。

これから色々積み上げていこうと思います。