FormBuilderを使って楽チンにエラーメッセージを表示しよう（メモ）

Webアプリケーションで入力フォームを作る場合の必須機能にバリデーション機能があると思います。今回は、バリデーション結果を各フォーム直下に表示した時の話を整理しようと思います。

ここについては、まだまだ未開の地が沢山。。。
間違っているや、もっと良い方法があるなどがあれば、ご指摘いただけると嬉しいです！
(特にHelperとBuilderの違いとか曖昧で理解が間違っているかも・・・）

# 前提
適用箇所：ユーザ登録に関するフォーム
環　　境：Ruby on Rails 5.2.1
　　　　 Devise(Railsのユーザ認証周りのgem)
テンプレートエンジン：haml

※バリデーションやdevise日本語化についての手順は割愛します。

# 全体的な流れ
Railsに用意されているFormHelperをカスタマイズして、エラーメッセージ表示用の要素を足しちゃおう。と言うのが大まかな流れです。他のQiitaで紹介されているものを参考にさせていただきました。

# FormHelperって？ FormBuilderって？
FormHelperと言って最も馴染み深いのがform_forではないでしょうか？
form_forは、お作法に従って記述するだけで簡単にコントローラへのパラメータ（params)渡しを実現できてしまうものです。ありがたや、ありがたや。

また、FormBuilderは、form_forで指定されたモデルと関連づけたフィールドを生成するためのもので、公式ドキュメントの日本語訳風に言うとform_helperとモデル間のプロキシ役をしてくれるもの。です。
カスタマイズした独自FormHelperへのルーティングをしてくれるイメージかな？

この辺りの話は、とりあえず公式ドキュメントの説明が一番わかりやすいと思いますので、それぞれのリンクを貼っておきます。
公式ドキュメント：[FormHelper](https://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormHelper.html)／[FormBuilder](https://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormBuilder.html)

# 実現したい内容
実現したいイメージはこんな感じです。
各フォーム内にいい感じにエラーが表示されてますね！
ウキウキしちゃいます（・ω・）
[![Image from Gyazo](https://i.gyazo.com/4df10ae65c78fe32b75282f345676ea2.png)](https://gyazo.com/4df10ae65c78fe32b75282f345676ea2)

# FormHelperをカスタマイズさせよう
早速カスタムさせるための記述を書きましょう。
FormHelperは通常のProjectツリーに記載はないので、ファイルの作成から始めます。ぶっちゃけどこでも良いのですが、FormHelperのカスタマイズ版なのでHelperフォルダ配下に新しいファイルを作ってあげましょうか。ファイル名も個人の好みに委ねます。今回の例では'cutom_form_helper.rb'としました。

フォルダ：app/helpers/cutom_form_helper.rb

実際のコードは↓。このコードの構成は大きく２つ。
　①エラーメッセージを含んだ要素を生成してあげる記述
　②既存のFormHelperをオーバーライドさせる記述

②から①を呼び出して、得られたデータ（エラーメッセージ要素）をくっつけてあげてます。

```custom_form_helper.rb
class CustomFormHelper < ActionView::Helpers::FormBuilder

  # ①エラーメッセージを含んだ要素を生成してあげる記述
  def pick_errors(attribute)
    return nil if @object.nil? || (messages = @object.errors.messages[attribute]).nil?

    lis = messages.collect do |message|
      %{<li>#{@object.errors.full_message(attribute, message)}</li>}
    end.join

    %{<ul class="errors">#{lis}</ul>}.html_safe
  end

  # ②既存のFormHelperをオーバーライドさせる記述
  def text_field(attribute, options={})
    return super if options[:no_errors]
    super + pick_errors(attribute)
  end

end
```

ここで紹介しているのはtext_field('=f.text_field・・・'のHelperメソッドにのみ対応）のみです。emailやpasswordなどHelperメソッドごとに記述が必要ですので、ご注意ください。

ちなみに「他って何があるの？」と思った方。
[公式ドキュメント]((https://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormHelper.html))や[GitHub](https://github.com/rails/rails/blob/bdc581616b760d1e2be3795c6f0f3ab4b1e125a5/actionview/lib/action_view/helpers/form_helper.rb)で公開されているものを参考にすると解決すると思います^^

ものによっては紹介したtext_fieldと引数が異なるものもあるので、単にコピペするだけではなくgithubを確認しながらをオススメします。
(必須でない引数も書かれてたりしますが）

# カスタマイズしたFormHelperを適用する
折角作ったものも適用しなければ意味がありません。
適用の仕方は至って簡単で、form_forやform_withの構文でプロキシ役であるBulderに該当のクラス名を指定してあげるだけ。
以下のような感じ↓（builder:'作ったクラス名'）

```sample.html.haml
  = form_with model:@user, url:xxx, builder: CustomFormHelper, method: :post, local: true  do |f|
    = f.text_field :nickname, placeholder:'例）カリカリ太郎'
```
※超抜粋してます

こうすることでform_forやform_withがレンダリングされるたびに、CustomFormHelperが呼び出されフォームフィールドを生成してくれます。
もし、指定したCustomFormHelperに該当するメソッドが存在しない場合でも、デフォルトのFormHelperが適用されだけで、エラーになることは無いようです。（経験則）

# 項目名の日本語化
この段階でdeviseの日本語化ができていれば、メッセージ自体は日本語になっていると思います。ただ、自分たちで作ったモデルの項目名までは流石に日本語化されません。
この段階だと↓みたいになってますよね。
[![Image from Gyazo](https://i.gyazo.com/d684e021f46746a0b62b3dbc72f509ac.png)](https://gyazo.com/d684e021f46746a0b62b3dbc72f509ac)

察しの良い人は「あっ！」ってなると思いますが、
モデルの項目に対し、設定ファイルで日本語名を指定してあげる必要があります。

ja.ymlに足しても良いのですが、私は「models.ja.yml」と言うファイルを新たに追加しました。（これも個人の好みでお願いします）

フォルダ：config/locales/

記載例は以下のとおり↓↓

```models.ja.yml
ja:
  activerecord:
    attributes:
      user:
        nickname: ニックネーム
        email: メールアドレス
        password: パスワード
        password_confirmation: パスワード（確認）
        current_password: 現在のパスワード
        lastname: 姓
        firstname: 名
        lastname_kana: 姓カナ
        firstname_kana: 名カナ
        birthday: 生年月日
        tel: 電話番号
      address:
        postal_code: 郵便番号
        prefecture_id: 都道府県
        city: 市区町村
        address_number: 番地
        building_name: 建物名
```

これで項目名も含めて日本語化されたはずです。

# CSSについて
どうせなら、該当フォームの枠を赤くしたいなぁ〜とか思いますよね。
Railsでは、エラーメッセージを含むフィールドは自動的にfield_with_errorsクラスを持つdivタグで囲まれます。これを利用して、エラーメッセージをもっと目立たせるようにcssルールを定義が可能です！
（エラーメッセージもCutomFormHelperで指定したクラスにCSS当ててあげてね）

↓以下がイメージ。

```sample.html

　<div class="field_with_errors">
   <input class="xxxx" placeholder="例）カリカリ太郎" type="text" name="user[nickname]" id="user_nickname">
  </div>
  <ul class="errors">
    <li>ニックネームを入力してください</li>
  </ul>

```

このfield_with_errorsに以下のようなcssを設定してあげればOKそうですね。
（あくまで例です。。。）

```sample.scss
.field_with_errors input{
  outline: 0;
  border-color:#ea352d;
  background-color:#fff;
}
```
ただ、この便利仕様も知らないとレイアウト崩れの原因になりそうで怖い・・・
実際に困った人が対処法をあげてました。気になった方は[コチラ](https://blog.naichilab.com/entry/2016/01/15/013321)。

# 未解決課題
以下のようにしたい場合にどうすれば良いか？の解決策が見つけられていません。
エラーになった段階でエラーメッセージ以外が別のdivで囲われてしまったり、エラーメッセージ表示時のulがinputフィールドと同列になってしまい、改行された状態になります。エラーもそれぞれのフォームごとに表示されるし・・・orz

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445937/997d0417-9903-664e-c75a-6ab86458ae31.png)

もう少し深くFormHelperとお付き合い出来れば、解決できるのかな・・・
CSSだけじゃどうにも・・・出来ても美しくないキット（；ω；）

# まとめ
FormHelperとFormBuilderはすごく便利な機能！

RailsのHelperメソッドってこんな感じで使えるのか。と言う良い気づきにな理ました。
Line風メッセージ機能もHelperメソッドで定義すれば簡単に行ける？・・・行けた！！と言う感じで応用の幅も広がりました。

個人的にhtml表記が散乱するのは好ましいと思えないので、フロントをRailsのViews,Helpers,Javascript（JQuery）でガリガリ書くのではなく、ReactやVue.js側に委ねるのが良いのかな。とも思いました。（Reactの参考書を少しかじっただけですが・・・）

引き続き勉強だ〜！
指摘などあれば、是非よろしくお願いいたします・・・！！！！

# 参考
Railsのフォームビルダーをカスタマイズして、入力フォームのそばにエラーメッセージを表示する:
https://www.subarunari.com/entry/2018/02/12/234447
各formごとにvalidationのエラーメッセージ出したい：
https://qiita.com/ytsukamoto/items/269a1a2b32462626190f