【Rails】Action View(ビュー)の書き方《フォーム篇》

2021年4月16日 13:16

はじめに

ActionView(ビュー)では、一般的なフォームを作成するための豊富なヘルパーメソッドが用意されています。Railsアプリにおいてフォームはなくてはならないものです。ユーザーの目に触れる部分でもあるので、フォームの良し悪しがアプリの評価にも繋がります。

本記事では、ActionView(ビュー)の機能のうち、フォームに関することをまとめています。

フォームの書き方

フォームのヘルパーメソッド

Rails 5まではフォームを設置するときにform_tag、あるいはform_forを使っていました。form_tagは関連するモデルがないときに使用し、form_forは関連するモデルがあるときに使用します。これらはフォームの中のフィールドの書き方にも差異があり、あまり使い勝手がいいとは言えませんでした。

Rails 5.1でform_withが追加されました。form_withは、それまでのform_tagform_forを統合したようなメソッドです。引数を指定することで、フォームとモデルの関連があるかないかをRailsに伝えます。

form_withの登場でform_tagform_forの仕様は非推奨となりました。ここでは、form_withの使い方について記述します。

form_withの書き方

form_withは関連するモデルの有無で書き方が変わります。関連するモデルがない場合の書き方は以下の通りです。

<%= form_with url: '/search' do |form| %>
  <%= form.text_field :search_text %>
  <%= form.submit %>
<% end %>

関連するモデルがある場合の書き方は以下の通りです。

<%= form_with model: @article do |form| %>
  <%= form.label :title %>
  <%= form.text_field :title, placeholder: '題名' %>

  <%= form.label :content %>
  <%= form.text_area :content, placeholder: '本文', rows: '10' %>

  <%= form.submit %>
<% end %>

form_withの第1引数がurlなら関連するモデルがない、modelなら関連するモデルがあるということになります。フォームの中のフィールドの書き方に差異はありません。

form_withのレコード識別

form_withにモデルのインスタンスを渡すと、モデルの中身にあわせて適切なフォームを構築します。モデルのインスタンスからモデル名のハッシュを作成し、コントローラーに送信します。コントローラーではフォームに入力された値をparams[:article][:title]のように取得することができます。

モデルのインスタンスが空の場合、フォームのメソッドをPOSTにし、適切なルーティングを設定します。また、モデルのインスタンスが空でない場合、フォームのメソッドをPATCHにし、適切なルーティングを設定します。そして各フィールドにインスタンスの各値を設定します。

これにより、フォームを作成画面と編集画面で分けて作成する必要がなくなり、共通のパーシャルとしてテンプレートに組み込むことができるようになります。パーシャルについては以下の記事を参照してください。

form_withの同期通信

form_withはデフォルトで非同期通信を行います。従来どおり同期通信にしたい場合は、以下のようにlocal引数を有効にします。

<%= form_with model: @article, local: true do |form| %>
  ...
<% end %>

フィールドのヘルパーメソッド

フォームには豊富なフィールドのヘルパーメソッドが用意されています。ここでは、よく使うフィールドの一部を紹介します。すべてのフィールドのヘルパーメソッドを確認したい場合や、フィールドのオプションをすべて確認したい場合は以下を参照してください。

フィールドの第1引数には属性名を指定します。関連モデルのあるフォームの場合、属性名はカラム名と言い換えても構いません。

label

labelはフィールドのラベルを作成します。label単体で使うことはなく、必ず他のフィールドとセットで使います。labelの属性名は、セットとなるフィールドの属性名と一致させる必要があります。ラベル内容を省略すると属性名(頭文字は大文字)が使用されます。

<%= form.label :name, '名前' %>
<%= form.text_field :name %>

text_field

text_fieldは通常のテキストボックスを作成します。maxlengthオプションで入力できる文字数を制限したり、プレースホルダーを設定できます。

<%= form.text_field :name, maxlength: 10, placeholder: '名前' %>

パスワードの入力にはpassword_field、メールアドレスの入力にはemail_fieldを使います。

<%= form.password_field :password, placeholder: 'パスワード' %>
<%= form.email_field :email, placeholder: 'メールアドレス' %>

email_fieldに入力された値は、送信時にブラウザによりフォーマットチェックが行われます(以下はGoogle Chromeの例)。

check_box

check_boxはチェックボックスを作成します。チェックボックスとラベルを横並びにするには、以下のようにlabelブロックで囲む必要があります。

<%= field_set_tag '好きな動物は?' do %>
  <%= form.label :check_dog do %>
    <%= form.check_box :check_dog, checked: true %><% end %>
  <%= form.label :check_cat do %>
    <%= form.check_box :check_cat %><% end %>
<% end %>

radio_button

radio_buttonはラジオボタンを作成します。ラジオボタンとラベルを横並びにするには、以下のようにlabelブロックで囲む必要があります。

<%= field_set_tag '同意しますか?' do %>
  <%= form.label :agree, value: 'yes' do %>
    <%= form.radio_button :agree, 'yes', checked: true %>
    はい
  <% end %>
  <%= form.label :agree, value: 'no' do %>
    <%= form.radio_button :agree, 'no' %>
    いいえ
  <% end %>
<% end %>

select

selectまたはcollection_selectはセレクトボックスを作成します。セレクトボックスのリストにモデルのレコードを使う場合、collection_selectを使います。

<%= form.collection_select :animals, @animals, :id, :name, prompt: "選択してください", selected: @animal.id %>

各引数は以下の通りです。

引数 説明 設定例
属性名 フィールドの属性名。 :animals
コレクション リストを含むモデルのインスタンス。 @animals
コレクションのキー リストのキーとなるカラム名。 :id
コレクションの値 リストの値となるカラム名。 :name
プロンプト リストの先頭に表示する文字列。 "選択してください"
初期値 リストの初期選択値。 @animal.id

セレクトボックスのリストにenumの値を使う場合、selectを使います。

<%= form.select :animals, Animal.names.map { |k, v| [v, k] }, prompt: "選択してください", selected: @animal.id %>

各引数は以下の通りです。

引数 説明 設定例
属性名 フィールドの属性名。 :animals
コレクション リストを含むenum変数(複数形)のマッピング。 Animal.names.map
コレクションのキーと値 リストのキーと値の組み合わせ。 { |k, v| [v, k] }
プロンプト リストの先頭に表示する文字列。 "選択してください"
初期値 リストの初期選択値。 @animal.id

file_field

file_fieldはファイル選択を作成します。file_fieldを含むフォームはmultipartを有効にする必要があります。

<%= form_with model: @article, multipart: true do |form| %>
  <%= form.file_field :image %>
<% end %>

他のフィールドと違い、file_fieldは初期値の設定ができません。例えば、@article.imageが設定されていたとしても、その値はfile_fieldに設定されません。これは、セキュリティ上の問題で<input type="file">タグには外部からアクセスできないためです。

編集画面などで@article.imageの中身を確認したい場合、以下のようにサムネイル表示しておくといいです。

<%= image_tag @article.image_url(:thumb) %>
<%= form.file_field :image %>

実際にファイルアップロードを行うには、ActiveStorageの設定やCarrierWaveの導入を行っておく必要があります。

フォームのエラー表示

モデルにバリデーションを設定しているとコントローラーでバリデーションチェックが行われ、結果が不適合だった場合はビューにエラーが返されます。

エラーメッセージはモデルのインスタンスの中のerrorsハッシュに自動で格納されます。フォームの最上部などにエラーメッセージを表示する処理を記述しておきます。以下の例では、何かしらのエラーがあった場合、エラーメッセージをリスト形式で表示しています。

<% if @article.errors.any? %>
  <h2>入力した値にエラーがあります。エラーを修正してください。</h2>
  <ul>
    <% @article.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

エラーメッセージをエラーのあるフィールドの近くに表示するには、以下のように記述します。

<%= form.text_field :title %>
<% if @article.errors.include?(:title) %>
  <p class="error_message">
    <%= @article.errors.full_messages_for(:title).first %>
  </p>
<% end %>

また、バリデーションチェックの結果が不適合だったフィールドを囲むfield_with_errorsクラスが自動で追加されます。これによりデザインが崩れるという場合は以下のスタイルを追加します。

.field_with_errors {
  display: contents;
}

なお、エラーメッセージを日本語化するには以下を参照してください。

まとめ

Railsのフォームタグヘルパーは、渡されたモデルのインスタンスを解析し、作成画面または編集画面のフォームを適切な形で構築してくれます。少ない記述で多くのことを自動でしてくれるため非常に使い勝手のいいメソッドなのですが、デフォルトの設定値などを把握しておかないと、思わぬ挙動を起こす可能性があります。

本記事を参考にして、ActionView(ビュー)のフォームの使い方を覚えていただければと思います。

関連記事

【Rails】Railsアプリのデバッグ《マルチデバイス篇》
# はじめに 近年のWebアプリはレスポンシブ対応が当たり前になっています。最低でもPCとスマートフォンに対応したデザイン、ときにはその中間のタブレットに対応したデザインなんかも作成する必要があります。 Webアプリの開発はPCを使って行う [...]
2021年5月23日 13:02
【Rails】Railsアプリのデバッグ《Better Errors篇》
# はじめに Railsアプリの開発中になんらかのエラーが発生すると、デフォルトでは以下のような画面が表示されます(画像をクリックすると拡大します)。 <a class="gallery" data-group="gallery" href [...]
2021年5月22日 19:42
【Rails】Railsアプリのデバッグ《byebug篇》
# はじめに Ruby on Railsに限りませんが、アプリの開発中にはエラーは付き物です。なにかしらのエラーが発生したときに、エラーの原因を特定しエラー箇所を修正することをデバッグと言います。Railsではデバッグの手助けとなる機能があらかじ [...]
2021年5月22日 15:30
【Rails】レンダリング(renderメソッド)でアンカー指定を行う
# はじめに 通常、Railsでアンカー付きのリクエストを発生させるには`redirect_to`を使います。 ```rb redirect_to root_path(anchor: 'target') ``` では、`rende [...]
2021年5月20日 10:47
【Rails】Bundler 2.2.x以降は開発者が適切なプラットフォームを追加する必要がある
# 事象 昔作ったRailsアプリを久しぶりに修正しデプロイしようとしたところ、以下のエラーが出力されました。 ```bash # 実行コマンド Running $HOME/.rbenv/bin/rbenv exec bundle ch [...]
2021年5月18日 16:18