ユーザー新規登録時にリアルタイムでエラーを出す方法

ユーザー新規登録の時、ちゃんと文字を入力したと思いきや入力できていなかったり、バリデーションの問題で登録ボタンを押したときに、弾かれたりすることってありますよね？

・・・ありますよね？

そんなときに入力した後にエラー文がその場で出てくれるとありがたいと思い機能を考えてみました。

# 1.流れの確認

じゃあやってみよう！
そういってもjavascript初心者の私にそのようなことが表現できるか不安だったので、まずは流れの確認から始めます。

前提として、`deviseを使用しております`。

まずはニックネームの場合のスタートとゴールを決めます。

start:登録フォームのニックネーム入力スペースに文字を入力する`

goal :バリデーションに引っかかっていたらニックネーム入力スペースの下にエラー文を出す
引っかかっていなければエラー文は出ない（既に出ていたら消える）


# 2.表現方法

- まずはエラー文の文字を出す方法<br />
幾つか方法はありますが、シンプルにしました。<br />

HTML側でニックネームフォームを少し変更します。

```ruby:registrations.html.erb

  <div class="field">
    <%= f.label :nickname %><br />
    <%= f.text_field :nickname, autocomplete: "email" %>
  </div>

```

```ruby:registrations.html.erb

  <div class="field">
    <%= f.label :nickname %><br />
    <%= f.text_field :nickname, autocomplete: "email", id:"registration-nickname" %>  ## idを設定
    <div id="nickname-error", class="red", hidden>※ニックネームの入力が必要です</div>　　##この一文を追加
  </div> 

```
と変更しました。

デフォルトでは`hidden`によって隠されていますが、追加されたこの一文を
`バリデーションに引っかかっていたらhiddenを消す`→『ニックネームの入力が必要です』の文字が出る
`引っかかっていなければhiddenを追加`→文字が消える

このように表現することにしました。

`id="~~"`はjs側で取得するid名なのでなんでもどうぞ。
`class="red"`は赤文字にしているだけです。

- バリデーションをどうするか<br />
  私が設定したニックネームのモデルで決めたバリデーションは`presence:true`のみです。
これをjs側でも読み込ませる必要があります。

最初考えたバリデーションなどを載せたかったのですが、色々やっているうちに忘れてしまったので、
決定したバリデーションのみ表示します。

```javascript

const nickNameValidate = /^[a-zA-Z0-9ぁ-んァ-ン一-龥ｧ-ﾝﾞﾟ]{1,}$/;
```

解説すると、
`[a-zA-Z0-9ぁ-んァ-ン一-龥ｧ-ﾝﾞﾟ]`とは
`小文字のa~z`,`大文字のA~Z`,`数字`,`ひらがな`、`カタカナ`、`漢字`、`半角カタカナ`
に対応します。

`{1,}`とは
まず、{x,y}とすると、`x文字以上`、`y文字以下`という設定ができます。
{x,}は`x文字以上`という設定のみで、{,y}だと`y文字以下`という設定のみにできます。
すなわち`{1,}`とは一文字以上という意味になります。

何故かわかりませんがこの記述がないとエラー文がでっぱなしになってしまいました。
おそらく設定していないと１文字のみに対応になってしまうのかも？
※要検証

# 3.jsでの処理を決める<br />
jsでの処理は以下の流れにしました。<br />

1:HTML側のニックネームフォームの要素を取得してくる
2:入力されたニックネームのバリデーションチェック
3:hiddenを追加するか削除するかの決定をif文で決める

# 4.実行
先に完成したコードを載せて解説していく感じにしたいと思います。

```javascript:error.js
if (document.URL.match( /sign_up/ )) {
  window.addEventListener('load',function(){
    const nickName = document.getElementById("registration-nickname");

    nickName.addEventListener("blur", e => { 
      const nickNameError = document.getElementById("nickname-error")
      const nickNameValidate = /^[a-zA-Z0-9ぁ-んァ-ン一-龥ｧ-ﾝﾞﾟ]{1,}$/;
      if (nickNameValidate.test(nickName.value)){
        nickNameError.setAttribute("hidden", true)
      } else {
        nickNameError.removeAttribute("hidden")
      }
    })
  }
}
```

まずは新規登録画面のみに対応させるために`(document.URL.match( /sign_up/ ))`
を使いました。
これはURLに`sign_up`が含まれるものにだけこの発火イベントが発生する設定です。

そして次に新規登録画面に遷移したら`"registration-nickname"`の要素をnickNameという名前に変換して取得しています。

次に`blur`のイベントが起きたら`"nickname-error"`の要素と、`先ほど設定したバリデーション`を取得します。

`blur`は『フォーカスが外れた時』というイベントハンドラーです。
`input`と迷ったのですが私はこちらを採用しました。
イベントハンドラーに関してはご自分の表現したいもので決めてください。

そして最後にif文でチェックします。

`nickNameValidate.test(nickName.value)`とは
`nickName.value(入力したニックネームの値）`に対して`nickNameValidate`というバリデーションチェックをしています。

結果、trueであれば、`nickNameError.setAttribute("hidden", true)`
すなわち、`nickNameError(取得してきた id="nickname-error")`にhiddenがセットされるのでエラー表示は消えます。

falseであれば、`nickNameError.removeAttribute("hidden")`
`nickNameError`のhiddenが消され、エラー表示が見えるようになる。

という仕掛けになっております。

# 5.応用

この流れであれば、emailやパスワードでもバリデーションの記述さえ変えればそのまま使用できます。
最初に追加した

```ruby

<div id="nickname-error", class="red", hidden>※ニックネームの入力が必要です</div>
```
の`ニックネーム`の部分を`パスワード`に変えてパスワードフォームの下にセットして、jsで処理内容を記述すればパスワードの部分は完成です。
emailやパスワード確認、そのほか自分で追加したカラムに関してもこれでいけます。

ただ、パスワード確認はif文が少し工夫が必要で、

```javascript

      const passWordConfirmationError = document.getElementById("password-confirmation-error")
      if (passWord.value == passWordConfirmation.value){
        passWordConfirmationError.setAttribute("hidden", true)
      } else {
        passWordConfirmationError.removeAttribute("hidden")
      }
```

となります。
`passWord.value`はパスワードフォームで入力した値、
`passWordConfirmation.value`はパスワード確認フォームで入力した値です。
この２つがイコールの時という分岐になっています。

# 6.終わりに
正直、同じようなコードが幾つも並ぶことになるので、あまり良い書き方には見えません。
ただ、今の私ではこれが精一杯です。jsにもクラスとインスタンスの概念があるようなので、このへんの理解を深めたら、もっと綺麗なコードに書き変えたいと思います。