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

2022年6月16日 18:21

はじめに

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(ビュー)のフォームの使い方を覚えていただければと思います。

関連記事

【Ruby】Bundlerを使ってRubyGemsを作成/公開する方法
# はじめに Bundlerを使ってRubyGemsを作成および公開する方法について説明します。Bundlerを使わずにRubyGemsを作成/公開する方法については以下の記事を参照してください。 <iframe class="hatena [...]
2022年7月12日 23:18
【Ruby】RubyGemsを作成/公開する方法
# はじめに RubyGemsを作成および公開する方法について説明します。Bundlerを使ってRubyGemsを作成する方法については以下の記事を参照してください。 <iframe class="hatenablogcard" style [...]
2022年7月11日 21:52
【Rails】M1チップ搭載MacでRuby on Railsの開発環境構築
# はじめに M1チップ搭載MacにRuby on Railsの開発環境を構築する手順を記載します。 - MacBook Air (M1, 2020) - macOS Monterey 12.3.1 # Homebrew ## [...]
2022年5月5日 11:56
【Rails】Rakeタスクの基本情報と作成・実行方法
# はじめに Railsには標準でRakeというGemが同梱されています。RakeはRubyで実装されたMake(UNIX系のOSで使用できるコマンド)のようなビルド作業を自動化するツールです。Ruby Make、略してRakeというわけですね。 [...]
2022年3月7日 22:12
【Rails】モデルに外部キーを設定する方法とよく起こるエラー内容について
# はじめに Railsでモデルに外部キーを設定する方法について説明します。 # モデルに外部キーを設定する ## リレーションシップ 今回は1つのブログ記事は複数のコメントを持つ1対多のリレーションシップを例に説明します。現在は` [...]
2022年2月10日 14:18
【Rails】Capybaraのfill_inメソッドを実行すると「既存レコードの内容+指定した内容」がセットされる事象の原因と対処【RSpec】
# はじめに RSpec + Capybaraを使用して、Railsアプリの統合テストを実装しています。とあるモデルの編集画面において、入力フォームの内容を書き換えた上で送信し、レコードが更新されることを確認します。 入力フォームの内容を書 [...]
2022年1月27日 21:22
【Rails】GitHubのセキュリティアラートで発見された脆弱性を解消する方法
# はじめに GitHubにはセキュリティアラートという機能があります。セキュリティアラートはリポジトリに含まれるライブラリやパッケージの脆弱性を定期的にチェックし、脆弱性のあるライブラリやパッケージが発見されたらアラートで知らせてくれるという機 [...]
2022年1月16日 10:36
【Rails】devise-two-factorを使った2段階認証の実装方法【初学者】
# はじめに Railsアプリで2段階認証を実装するには、「rotp」というGemを使う方法の他に、「devise-two-factor」というGemを使う方法があります。「devise-two-factor」はその名の通り、IDとパスワードによ [...]
2021年12月12日 17:58