ウィザード形式でユーザ登録のフォームを作成してみた③-2　〜バリデーション(エラー表示)〜

## 【この記事の説明】
現在、私はプログラミングスクールでプログラミング（主にRuby）の勉強をしています。
そして、その一環でチーム開発（メルカリもどきのサイト作成）を行っており、私はその中で記事のタイトルにあるように、ウィザード形式でユーザの登録ができるようにする作業を担当致しました。
又、スクールからの指示で、この登録フォームにはdeviseを使用するようにとのことでした。

この記事は、その担当作業についてまとめたものになります。
## 【作業】
ここから私が行った作業（そこで困ったこと）を記事として記載していこうと思います
※この記事では、バリデーションのエラー表示について記載したいと思います

#### エラー表示
今回のエラー表示には、deviseに元々あるファイルを使用したものと、それとは別に作成したファイルを使用したものの２種類があります

**エラー表示を日本語表示にする**
ユーザがエラーを読み易いように、日本語表示にする為、gemをインストールします
この時、必ず、Gemfileで、```group :development do〜end```や```group :test do~end```の中に記述しないように気をつけます

```ruby:/Gemfile
# deviseの日本語対応gem↓
gem 'devise-i18n'
gem 'devise-i18n-views'
# railsを日本語対応にする↓
gem 'rails-i18n'
```

**deviseの場合**
では、まずdeviseの場合です

devise.views.ja.ymlに必要な部分を追加します

```ruby:/config/locales/devise.views.ja.yml
ja:
  activerecord:
    attributes:
      user:
        current_password: "現在のパスワード"
        email: "メールアドレス"
        password: "パスワード"
        password_confirmation: "確認用パスワード"
        remember_me: "ログインを記憶"
        # 追加部分↓
        nickname: "ニックネーム"
        family_name: "名字"
        given_name: "名前"
        family_name_kana: "名字（カナ）"
        given_name_kana: "名前（カナ）"
        birthday: "生年月日 "
        postal_code: "郵便番号"
        prefectures: "都道府県"
        city: "市区町村"
        address: "番地"
        building: "マンション名など"
        phone_number: "電話番号"
        # 追加部分↑
    models:
      user: "ユーザ"
      # 追加部分↓
      profileaddress: "ご本人様情報入力（任意）"
      deliveryaddress: "お届け先入力"
      # 追加部分↑
  devise:
    confirmations:
      new:
        resend_confirmation_instructions: "アカウント確認メール再送"
    mailer:
      confirmation_instructions:
        action: "アカウント確認"
        greeting: "ようこそ、%{recipient}さん!"
        instruction: "次のリンクでメールアドレスの確認が完了します:"
      reset_password_instructions:
        action: "パスワード変更"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "誰かがパスワードの再設定を希望しました。次のリンクでパスワードの再設定が出来ます。"
        instruction_2: "あなたが希望したのではないのなら、このメールは無視してください。"
        instruction_3: "上のリンクにアクセスして新しいパスワードを設定するまで、パスワードは変更されません。"
      unlock_instructions:
        action: "アカウントのロック解除"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "アカウントのロックを解除するには下のリンクをクリックしてください。"
        message: "ログイン失敗が繰り返されたため、アカウントはロックされています。"
    passwords:
      edit:
        change_my_password: "パスワードを変更する"
        change_your_password: "パスワードを変更"
        confirm_new_password: "確認用新しいパスワード"
        new_password: "新しいパスワード"
      new:
        forgot_your_password: "パスワードを忘れましたか?"
        send_me_reset_password_instructions: "パスワードの再設定方法を送信する"
    registrations:
      edit:
        are_you_sure: "本当に良いですか?"
        cancel_my_account: "アカウント削除"
        currently_waiting_confirmation_for_email: "%{email} の確認待ち"
        leave_blank_if_you_don_t_want_to_change_it: "空欄のままなら変更しません"
        title: "%{resource}編集"
        unhappy: "気に入りません"
        update: "更新"
        we_need_your_current_password_to_confirm_your_changes: "変更を反映するには現在のパスワードを入力してください"
      new:
        sign_up: "アカウント登録"
    sessions:
      new:
        sign_in: "ログイン"
    shared:
      links:
        back: "戻る"
        didn_t_receive_confirmation_instructions: "アカウント確認のメールを受け取っていませんか?"
        didn_t_receive_unlock_instructions: "アカウントの凍結解除方法のメールを受け取っていませんか?"
        forgot_your_password: "パスワードを忘れましたか?"
        sign_in: "ログイン"
        sign_in_with_provider: "%{provider}でログイン"
        sign_up: "アカウント登録"
    unlocks:
      new:
        resend_unlock_instructions: "アカウントの凍結解除方法を再送する"
```
viewでエラーを呼び出すコードを記述します
ここで、deviseの記載そのままだと``= form_with``の下に``= render``の形ですでに記述されています
この記述により、エラー用のファイルが呼び出されて、このview（new.html.haml）のエラーが全て上に表示されますが、今回は入力フォーム毎にエラーを表示したいので、この記述``= render``は削除します

そして、入力フォーム下に```- if @user.errors.any?```を記述し、エラー表示を入力フォーム下にするようにします
ちなみに、この```- if @user.errors.any?```の記述内容は、```/app/views/users/shared/_error_messages.html.haml```をほぼ引用したものであり、異なっているのは、```- @user.errors.full_messages_for(:nickname).each do |message|```の部分となります

``full_messages_for(xxx)``は特定の属性（xxx）のエラーメッセージのみを取得するメソッドであり、この場合は``:nickname``に関するエラーメッセージのみ表示するということになります



```ruby:/app/views/users/registrations/new.html.hamlから必要部分のみ抜粋
.login-main__title
      会員情報入力
    .login-main__content
      .login-main__content__form
        = form_with(model: @user, url: profileaddresses_path,local: true,method: :post) do |f|  
        # = render "devise/shared/error_messages", resource: resource
        # deviseの記載そのままだと上のコードがあるが、ここにあると全てのエラーがフォームの一番上に表示されてしまう為、削除
.form-group
            ニックネーム
            %span.required
              必須
            %br
            .field
              = f.text_field :nickname, autofocus: true, autocomplete: "nickname",placeholder: "例）フリマ太郎",class: "default" 
              # ここから↓記述           
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:nickname).each do |message|
                      %li{style: "color:red"}
                        = message
               # ここまで↑記述
```
```ruby:/app/views/users/shared/_error_messages.html.haml
- if resource.errors.any?
  #error_explanation
    %h2
      = I18n.t("errors.messages.not_saved",                 |
        count: resource.errors.count,                       |
        resource: resource.class.model_name.human.downcase) |
    %ul
      - resource.errors.full_messages.each do |message|
        %li= message

```

``= form_withの下``
<img width="453" alt="スクリーンショット 2020-05-29 5.36.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573113/c7d080ac-0365-cb5f-59ce-bc1367097b38.png">

``入力フォーム下``
<img width="450" alt="スクリーンショット 2020-06-08 5.36.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573113/f9bec825-2e13-4dee-e705-ab28cfd3e000.png">


**deviseを使用できない場合**
``/config/locales/ja.yml``に必要部分を追加します

```ruby:/config/locales/ja.yml(追加部分のみ記載)
ja:
  activerecord:
    # 追加部分↓
    profileaddresses:
      postal_code: '郵便番号'
      prefectures: '都道府県 '
      city: '市区町村'
      address: '番地'
      building: 'マンション名など'
    deliveryaddresses:
      family_name: "名字"
      given_name: "名前"
      family_name_kana: "名字（カナ）"
      given_name_kana: "名前（カナ）"
      birthday: "生年月日 "
      postal_code: '郵便番号'
      prefectures: '都道府県 '
      city: '市区町村'
      address: '番地'
      building: 'マンション名など'
      phone_number: '電話番号'
    # 追加部分↑
    errors:
      messages:
        record_invalid: 'バリデーションに失敗しました: %{errors}'
        restrict_dependent_destroy:
          has_one: "%{record}が存在しているので削除できません"
          has_many: "%{record}が存在しているので削除できません"
```

バリデーションを日本語使用にする為に、I18n.t(User.human_attribute_name(xxx))を使います

```ruby:/app/models/deliveryaddress.rb(必要部分のみ抜粋)
  def name_em
    @error_name1 = I18n.t(User.human_attribute_name(:family_name))
    # I18n.t(User.human_attribute_name(:family_name)) = Translation missing: ja 名字
    @error_name2 = I18n.t(User.human_attribute_name(:given_name))
    # I18n.t(User.human_attribute_name(:given_name)) = Translation missing: ja 名前
    if !(family_name =~ /^[ぁ-んァ-ン一-龥]/)
      if !(given_name =~ /^[ぁ-んァ-ン一-龥]/)
        errors.add @error_name1, "は全角で入力してください"
        errors.add @error_name2, "は全角で入力してください"
      else
        errors.add @error_name1, "は全角で入力してください"
      end
    elsif !(given_name =~ /^[ぁ-んァ-ン一-龥]/)
      errors.add @error_name2, "は全角で入力してください"
    else
      return
    end
  end

```

そのままエラーメッセージを表示すると"Translation missing: ja"という言葉が表示されてしまうので、それを削除するコードを記述をします

```ruby:/app/views/users/registrations/new_deliveryaddresses.html.haml(必要部分のみ抜粋)
  .login-main
    .login-main__title
      お届け先住所入力
    .login-main__content
      .login-main__content__form
        = form_with(model: @deliveryaddress,url: user_registration_path,local: true) do |f|
          
          .form-group
            お名前(全角)
            %span.required
              必須
            %br
            .field
              = f.text_field :family_name, autofocus: true, autocomplete: "family_name",placeholder: "例）山田",class: "default half"           
              = f.text_field :given_name, autofocus: true, autocomplete: "given_name",placeholder: "例）太郎",class: "default half__right"       
              -if @deliveryaddress.errors.any?
              # @deliveryaddressは/app/models/deliveryaddress.rbのこと
                .alert.alert-warning
                  %ul
                    - @deliveryaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("名字は")
                        # エラーメッセージ内に”名字はという言葉が入っている時は”
                          = message.delete!("Translation missing: ja")
                        # エラーメッセージ文の中から"Translation missing: ja"を削除する
                        # エラーメッセージが「Translation missing: ja 名字は全角で入力してください」が「名字は全角で入力してください」になる
              -if @deliveryaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @deliveryaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("名前は")
                         # エラーメッセージ内に”名前はという言葉が入っている時は”
                          = message.delete!("Translation missing: ja")
                         # エラーメッセージ文の中から"Translation missing: ja"を削除する
                         # エラーメッセージが「Translation missing: ja 名前は全角で入力してください」が「名前は全角で入力してください」になる

```

```= form_withの下``
<img width="470" alt="スクリーンショット 2020-05-29 5.39.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573113/c7af72fa-cc7d-258b-7b44-b27611c5473a.png">

``入力フォーム下``
<img width="460" alt="スクリーンショット 2020-05-31 21.08.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573113/51d3e9af-0bf2-3b7e-68b5-65a22ee89d3a.png">


###【参考】
[【Rails】バリデーションエラーをそのインプットフィールドの近くに１つだけ表示したかった。](https://qiita.com/at-946/items/6cf11f100951de67d085)
[あなたはいくつ知っている？Rails I18nの便利機能大全！](https://qiita.com/Kta-M/items/bd4ba36a58ad602a9d8b)
[Rails】バリデーションのエラーメッセージを取得・表示・日本語化する方法を完全解説！](https://techtechmedia.com/error-messages-validation-rails/)