0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Javascriptでバリデーションを実装

Last updated at Posted at 2024-07-17

はじめに

前回に引き続きbootstrapのモーダル機能で新規登録・ログインフォームを作成方法をアウトプットします✍️

初心者ですので、もっといい方法があるよ!
等ございましたらご教示いただけますと幸いです!

開発環境

  • ruby: 3.1.2
  • Rails: 6.1.7.7
  • Cloud9
  • bootstrap: 4.6.2

前提条件

  • bootstrap4.6.2 導入済み
  • devise 導入済み

Javascriptでバリデーションを実装

新規登録やログインにエラーが発生した時、
deviseの使用上元の(モーダルでない)新規登録ページやログインページに飛んでしまいます
ログインを非同期で行うことも試みましたが挫折しました…
なので、今回はJavascriptで簡単なバリデーションをつけていきたいと思います
コード全体だけ見たいよ〜という方はこちらをクリックしてください↓

Javascriptコード全体
application.js
document.addEventListener('turbolinks:load', () => {

  // 新規登録バリデーション
  const registrationSubmitCheck = document.querySelector('#registration_submit');
  if (registrationSubmitCheck) {

    let nameError = false;
    let emailError = false;
    let passError = false;
    let passConfirmError = false;

    const registrationName = document.querySelector('#registration_name');
    const registrationEmail = document.querySelector('#registration_email');
    const registrationPass = document.querySelector('#registration_pass');
    const registrationPassConfirm = document.querySelector('#registration_pass_confirm');
    const registrationSubmit = document.querySelector('#registration_submit');

    const inputs = [registrationName, registrationEmail, registrationPass, registrationPassConfirm];

    inputs.forEach(input => {
      input.addEventListener('input', () => {
        updateSubmit();
      });
    });

    // ユーザー名のinputイベントを監視
    registrationName.addEventListener('input', function() {
      let name = this.value;
      if (name.length < 1) {
        nameError = true;
        document.querySelector('#nameError').textContent = 'ユーザー名を入力してください。';
        document.querySelector('#nameError').style.display = 'block';
        updateSubmit();
      } else {
        fetch(`/users/check_name?name=${name}`)
          .then(response => response.json())
          .then(data => {
            if (data.status === false) {
              nameError = true;
              document.querySelector('#nameError').textContent = 'このユーザー名は既に登録されています。';
              document.querySelector('#nameError').style.display = 'block';
            } else {
              nameError = false;
              document.querySelector('#nameError').style.display = 'none';
            }
            updateSubmit();
          })
          .catch(error => {
            console.log("AJAXリクエスト失敗:", error);
          });
      }
    });

    registrationEmail.addEventListener('input', function() {
      let email = this.value;
      let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        emailError = true;
        document.querySelector('#emailError').textContent = 'メールアドレスの形式が正しくありません。';
        document.querySelector('#emailError').style.display = 'block';
        updateSubmit();
      } else {
        fetch(`/users/check_email?email=${email}`)
          .then(response => response.json())
          .then(data => {
            if (data.status === false) {
              emailError = true;
              document.querySelector('#emailError').textContent = 'このメールアドレスは既に登録されています。';
              document.querySelector('#emailError').style.display = 'block';
            } else {
              emailError = false;
              document.querySelector('#emailError').style.display = 'none';
            }
            updateSubmit();
          })
          .catch(error => {
            console.log("AJAXリクエスト失敗:", error);
          });
      }
    });

    // パスワードフィールドのinputイベントを監視
    registrationPass.addEventListener('input', function() {
      if (this.value.length < 6) {
        passError = true;
        updatePasswordError();
        document.querySelector('#passError').style.display = 'block';
      } else {
        passError = false;
        document.querySelector('#passError').style.display = 'none';
      }
      updateSubmit();
    });

    // パスワード最低文字数カウント
    function updatePasswordError() {
      let remaining = 6 - registrationPass.value.length;
      document.querySelector('#remainingChars').textContent = remaining;
    }

    // パスワード確認フィールドのinputイベントを監視
    registrationPassConfirm.addEventListener('input', function() {
      if (this.value !== registrationPass.value) {
        passConfirmError = true;
        document.querySelector('#passConfirmError').style.display = 'block';
      } else {
        passConfirmError = false;
        document.querySelector('#passConfirmError').style.display = 'none';
      }
      updateSubmit();
    });

    function updateSubmit() {
      if (registrationName.value.trim() === '' ||
          registrationEmail.value.trim() === '' ||
          registrationPass.value.trim() === '' ||
          registrationPassConfirm.value.trim() === '' ||
          nameError || emailError || passError || passConfirmError) {
        registrationSubmit.classList.add('disabled');
        registrationSubmit.setAttribute('disabled', true);
      } else {
        registrationSubmit.classList.remove('disabled');
        registrationSubmit.removeAttribute('disabled');
      }
    }
  }

  // ログインバリデーション
  const loginSubmitCheck = document.querySelector('#login_submit');
  if (loginSubmitCheck) {

    let loginEmailError = false;
    let loginPassError = false;
    let loginError = false;

    document.querySelectorAll('#login_email, #login_pass').forEach(element => {
      element.addEventListener('input', function() {
        if (document.querySelector('#login_email').value.trim() === '' || document.querySelector('#login_pass').value.trim() === '') {
          document.querySelector('#login_submit').classList.add('disabled');
          document.querySelector('#login_submit').setAttribute('disabled', true);
        } else if (loginEmailError || loginPassError || loginError) {
          document.querySelector('#login_submit').classList.add('disabled');
          document.querySelector('#login_submit').setAttribute('disabled', true);
        } else {
          document.querySelector('#login_submit').classList.remove('disabled');
          document.querySelector('#login_submit').removeAttribute('disabled');
        }
      });
    });

    // メールアドレスのinputイベントを監視
    document.querySelector('#login_email').addEventListener('input', function() {
      let email = this.value;
      let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        document.querySelector('#loginEmailError').textContent = 'メールアドレスの形式が正しくありません。';
        document.querySelector('#loginEmailError').style.display = 'block';
        loginEmailError = true;
      } else {
        document.querySelector('#loginEmailError').style.display = 'none';
        loginEmailError = false;
      }
      updateLoginSubmit();
    });

    // パスワードフィールドのinputイベントを監視
    document.querySelector('#login_pass').addEventListener('input', function() {
      if (this.value.length < 6) {
        updateLoginPasswordError();
        document.querySelector('#loginPassError').style.display = 'block';
        loginPassError = true;
      } else {
        document.querySelector('#loginPassError').style.display = 'none';
        loginPassError = false;
      }
      updateLoginSubmit();
    });

    // パスワード最低文字数カウント
    function updateLoginPasswordError() {
      let remaining = 6 - document.querySelector('#login_pass').value.length;
      document.querySelector('#loginRemainingChars').textContent = remaining;
    }

    function updateLoginSubmit() {
      if (document.querySelector('#login_email').value.trim() === '' || document.querySelector('#login_pass').value.trim() === '' || loginEmailError || loginPassError) {
        document.querySelector('#login_submit').classList.add('disabled');
        document.querySelector('#login_submit').setAttribute('disabled', true);
      } else {
        document.querySelector('#login_submit').classList.remove('disabled');
        document.querySelector('#login_submit').removeAttribute('disabled');
      }
    }

    document.addEventListener("ajax:success", function(e) {
      let data = e.detail[0];
      if (data.status === true) {
        location.href = "/";
      } else if (data.status === false) {
        document.querySelector('#loginError').textContent = 'メールアドレスまたはパスワードが間違っています。';
        document.querySelector('#loginError').style.display = 'block';
      } else if (data.status === "inactive") {
        document.querySelector('#loginError').textContent = '退会済みです。別のメールアドレスをお使いください。';
        document.querySelector('#loginError').style.display = 'block';
      }
    });

    document.addEventListener("ajax:error", function(e) {
      document.querySelector('#loginError').textContent = '通信エラーが発生しました。';
      document.querySelector('#loginError').style.display = 'block';
    });

  }
});

新規登録バリデーション

完成図
Image from Gyazo

新規登録バリデーション Javascriptコード全体
application.js
  // 新規登録バリデーション
  const registrationSubmitCheck = document.querySelector('#registration_submit');
  if (registrationSubmitCheck) {

    let nameError = false;
    let emailError = false;
    let passError = false;
    let passConfirmError = false;

    const registrationName = document.querySelector('#registration_name');
    const registrationEmail = document.querySelector('#registration_email');
    const registrationPass = document.querySelector('#registration_pass');
    const registrationPassConfirm = document.querySelector('#registration_pass_confirm');
    const registrationSubmit = document.querySelector('#registration_submit');

    const inputs = [registrationName, registrationEmail, registrationPass, registrationPassConfirm];

    inputs.forEach(input => {
      input.addEventListener('input', () => {
        updateSubmit();
      });
    });

    // ユーザー名のinputイベントを監視
    registrationName.addEventListener('input', function() {
      let name = this.value;
      if (name.length < 1) {
        nameError = true;
        document.querySelector('#nameError').textContent = 'ユーザー名を入力してください。';
        document.querySelector('#nameError').style.display = 'block';
        updateSubmit();
      } else {
        fetch(`/users/check_name?name=${name}`)
          .then(response => response.json())
          .then(data => {
            if (data.status === false) {
              nameError = true;
              document.querySelector('#nameError').textContent = 'このユーザー名は既に登録されています。';
              document.querySelector('#nameError').style.display = 'block';
            } else {
              nameError = false;
              document.querySelector('#nameError').style.display = 'none';
            }
            updateSubmit();
          })
          .catch(error => {
            console.log("AJAXリクエスト失敗:", error);
          });
      }
    });

    registrationEmail.addEventListener('input', function() {
      let email = this.value;
      let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        emailError = true;
        document.querySelector('#emailError').textContent = 'メールアドレスの形式が正しくありません。';
        document.querySelector('#emailError').style.display = 'block';
        updateSubmit();
      } else {
        fetch(`/users/check_email?email=${email}`)
          .then(response => response.json())
          .then(data => {
            if (data.status === false) {
              emailError = true;
              document.querySelector('#emailError').textContent = 'このメールアドレスは既に登録されています。';
              document.querySelector('#emailError').style.display = 'block';
            } else {
              emailError = false;
              document.querySelector('#emailError').style.display = 'none';
            }
            updateSubmit();
          })
          .catch(error => {
            console.log("AJAXリクエスト失敗:", error);
          });
      }
    });

    // パスワードフィールドのinputイベントを監視
    registrationPass.addEventListener('input', function() {
      if (this.value.length < 6) {
        passError = true;
        updatePasswordError();
        document.querySelector('#passError').style.display = 'block';
      } else {
        passError = false;
        document.querySelector('#passError').style.display = 'none';
      }
      updateSubmit();
    });

    // パスワード最低文字数カウント
    function updatePasswordError() {
      let remaining = 6 - registrationPass.value.length;
      document.querySelector('#remainingChars').textContent = remaining;
    }

    // パスワード確認フィールドのinputイベントを監視
    registrationPassConfirm.addEventListener('input', function() {
      if (this.value !== registrationPass.value) {
        passConfirmError = true;
        document.querySelector('#passConfirmError').style.display = 'block';
      } else {
        passConfirmError = false;
        document.querySelector('#passConfirmError').style.display = 'none';
      }
      updateSubmit();
    });

    function updateSubmit() {
      if (registrationName.value.trim() === '' ||
          registrationEmail.value.trim() === '' ||
          registrationPass.value.trim() === '' ||
          registrationPassConfirm.value.trim() === '' ||
          nameError || emailError || passError || passConfirmError) {
        registrationSubmit.classList.add('disabled');
        registrationSubmit.setAttribute('disabled', true);
      } else {
        registrationSubmit.classList.remove('disabled');
        registrationSubmit.removeAttribute('disabled');
      }
    }
  }

ブロックに分けて記載していきます

登録ボタンについて

まず前提として、新規登録フォームのsubmitボタンはデフォルトで表示された時クリックできないように実装しています

public/registarations/_form.html.erb
<%= f.submit "登録", disabled: true, class: "disabled", id: "registration_submit" %>
application.css
.disabled {
  cursor: not-allowed;
}

HTMLの機能でdisabled: trueと記述するだけです!
自動でボタンの色がグレーっぽくなります
class: "disabled"でcssの記述によりホバーした時にカーソルがバツになります

Image from Gyazo

application.js
  const registrationSubmitCheck = document.querySelector('#registration_submit');
  if (registrationSubmitCheck) {

    let nameError = false;
    let emailError = false;
    let passError = false;
    let passConfirmError = false;

    const registrationName = document.querySelector('#registration_name');
    const registrationEmail = document.querySelector('#registration_email');
    const registrationPass = document.querySelector('#registration_pass');
    const registrationPassConfirm = document.querySelector('#registration_pass_confirm');
    const registrationSubmit = document.querySelector('#registration_submit');

    const inputs = [registrationName, registrationEmail, registrationPass, registrationPassConfirm];

    inputs.forEach(input => {
      input.addEventListener('input', () => {
        updateSubmit();
      });
    });
  
:

    function updateSubmit() {
      if (registrationName.value.trim() === '' ||
          registrationEmail.value.trim() === '' ||
          registrationPass.value.trim() === '' ||
          registrationPassConfirm.value.trim() === '' ||
          nameError || emailError || passError || passConfirmError) {
        registrationSubmit.classList.add('disabled');
        registrationSubmit.setAttribute('disabled', true);
      } else {
        registrationSubmit.classList.remove('disabled');
        registrationSubmit.removeAttribute('disabled');
      }
    }

let nameError = false;などは、エラー状態かを表します
falseだとエラーでない状態、trueだとエラー状態を意味します
初期値はエラーでないfalseです

application.js
    const registrationName = document.querySelector('#registration_name');
    const registrationEmail = document.querySelector('#registration_email');
    const registrationPass = document.querySelector('#registration_pass');
    const registrationPassConfirm = document.querySelector('#registration_pass_confirm');
    const registrationSubmit = document.querySelector('#registration_submit');

    const inputs = [registrationName, registrationEmail, registrationPass, registrationPassConfirm];

    inputs.forEach(input => {
      input.addEventListener('input', () => {
        updateSubmit();
      });
    });

このコードは4つの入力フィールドのinputイベント(入力イベント)を監視し、
フィールドに何かしらの入力があった場合はupdateSubmit関数が呼び出されます

application.js
    function updateSubmit() {
      if (registrationName.value.trim() === '' ||
          registrationEmail.value.trim() === '' ||
          registrationPass.value.trim() === '' ||
          registrationPassConfirm.value.trim() === '' ||
          nameError || emailError || passError || passConfirmError) {
        registrationSubmit.classList.add('disabled');
        registrationSubmit.setAttribute('disabled', true);
      } else {
        registrationSubmit.classList.remove('disabled');
        registrationSubmit.removeAttribute('disabled');
      }
    }

updateSubmit関数では以下のことが行われています
.value.trim() === ''で4つのフィールドが空白でないか確認します
 (.trim()は、前後の空白を取り除く)
・エラーフラグを確認します
 (nameErrorなど4つ全てがエラー状態でないことを確認)
・エラーが一つでもある場合登録ボタンはクリックできないまま(一つでもtrueがある場合)
・エラーが一つもない場合登録ボタンをクリックできるようにする(全てfalseだった場合)
 (classのdisabledを削除して、disabled属性をfalseに変える↓)

- <%= f.submit "登録", disabled: true, class: "disabled", id: "registration_submit" %>
+ <%= f.submit "登録", disabled: false, class: "", id: "registration_submit" %>

また、Javascriptにおいて=====の違いは以下の記事がわかりやすかったです

ユーザー名のバリデーション(ajax使うよ)
application.js
    // ユーザー名のinputイベントを監視
    registrationName.addEventListener('input', function() {
      let name = this.value;
      if (name.length < 1) {
        nameError = true;
        document.querySelector('#nameError').textContent = 'ユーザー名を入力してください。';
        document.querySelector('#nameError').style.display = 'block';
        updateSubmit();
      } else {
        fetch(`/users/check_name?name=${name}`)
          .then(response => response.json())
          .then(data => {
            if (data.status === false) {
              nameError = true;
              document.querySelector('#nameError').textContent = 'このユーザー名は既に登録されています。';
              document.querySelector('#nameError').style.display = 'block';
            } else {
              nameError = false;
              document.querySelector('#nameError').style.display = 'none';
            }
            updateSubmit();
          })
          .catch(error => {
            console.log("AJAXリクエスト失敗:", error);
          });
      }
    });
application.js
    registrationName.addEventListener('input', function() {
      let name = this.value;
      if (name.length < 1) {
        nameError = true;
        document.querySelector('#nameError').textContent = 'ユーザー名を入力してください。';
        document.querySelector('#nameError').style.display = 'block';
        updateSubmit();

ユーザー名の入力フィールドのinputイベント(入力イベント)を監視し、
何かしら入力された後に、deleteなどで文字数が1未満になった時、
「ユーザー名を入力してください。」というエラー文がnameErrorのidが付与されている箇所に表示されます↓

public/registrations/_form.html.erb
<%= f.text_field :name, id: "registration_name"%>
<div id="nameError""></div>

次に、条件分岐で1文字以上入力されている時の処理です

application.js
      } else {
        fetch(`/users/check_name?name=${name}`)
          .then(response => response.json())
          .then(data => {

ajaxでurlから欲しいデータを取得します
そのためにはルーティングとuserコントローラにアクションを記述する必要があります
今回はpublicとadminにルーティングを分けていますのでご留意ください

routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'public/registrations',
    sessions: 'public/sessions',
  }
  :
  scope module: :public do
  :
    get 'users/check_name' => 'users#check_name'
  end
  :
end
users_controllre.rb
class Public::UsersController < ApplicationController
  before_action :authenticate_user!, except: [:check_name]
:
  def check_name
    name = params[:name]
    user = User.find_by(name: name)
    render json: user.nil? ? {status: true}.to_json : {status: false}.to_json
  end
:
end

フォームに入力されたユーザー名をfine_byで検索
ユーザーがnilの場合は{status: true}をjson形式返し、入力されたユーザー名と同一のユーザー名がデータベースに保存されている場合は、{status: false}をjson形式で返します

application.js
            if (data.status === false) {
              nameError = true;
              document.querySelector('#nameError').textContent = 'このユーザー名は既に登録されています。';
              document.querySelector('#nameError').style.display = 'block';
            } else {
              nameError = false;
              document.querySelector('#nameError').style.display = 'none';
            }
            updateSubmit();
          })
          .catch(error => {
            console.log("AJAXリクエスト失敗:", error);
          });

もしajaxで取得したデータが {status: false}の場合
nameErrortrueにします(エラー状態を意味します)
・「このユーザー名は既に登録されています。」というエラー文がnameErrorのidが付与されている箇所に表示されます↓

public/registrations/_form.html.erb
<%= f.text_field :name, id: "registration_name"%>
<div id="nameError""></div>

もしajaxで取得したデータが {status: true}の場合
nameErrorのidが付与されている箇所が非表示になります

もしajaxで取得したデータが {status: error}の場合
・AJAXリクエスト失敗とコンソールに表示されます

メールアドレスのバリデーション

最初の部分以外はユーザー名と同じなので省略します

application.js
    registrationEmail.addEventListener('input', function() {
      let email = this.value;
      let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        emailError = true;
        document.querySelector('#emailError').textContent = 'メールアドレスの形式が正しくありません。';
        document.querySelector('#emailError').style.display = 'block';
        updateSubmit();

ここで正規表現を利用して、「⚪︎⚪︎@⚪︎⚪︎.⚪︎⚪︎」の形式であることをチェックしています

パスワードのバリデーション
application.js
    // パスワードフィールドのinputイベントを監視
    registrationPass.addEventListener('input', function() {
      if (this.value.length < 6) {
        passError = true;
        updatePasswordError();
        document.querySelector('#passError').style.display = 'block';
      } else {
        passError = false;
        document.querySelector('#passError').style.display = 'none';
      }
      updateSubmit();
    });

    // パスワード最低文字数カウント
    function updatePasswordError() {
      let remaining = 6 - registrationPass.value.length;
      document.querySelector('#remainingChars').textContent = remaining;
    }

入力されたパスワードが6文字以上かチェックしています
6文字未満の場合passErrorのidが付与されているエラー文が表示され、
6から入力された文字数を引いた数をremainingCharsのidが付与されている箇所に挿入します↓

public/registrations/_form.html.erb
<%= f.password_field :password, autocomplete: "new-password", id: "registration_pass" %>
<div id="passError">パスワードを入力してください(あと<span id="remainingChars"></span>文字)。</div>
パスワード確認のバリデーション
application.js
    registrationPassConfirm.addEventListener('input', function() {
      if (this.value !== registrationPass.value) {
        passConfirmError = true;
        document.querySelector('#passConfirmError').style.display = 'block';
      } else {
        passConfirmError = false;
        document.querySelector('#passConfirmError').style.display = 'none';
      }
      updateSubmit();
    });

入力されたパスワード確認をパスワードと一致するかチェックし、一致しなければ
passConfirmErrorのidが付与されているエラー文が表示されます↓

public/registrations/_form.html.erb
<%= f.password_field :password_confirmation, autocomplete: "new-password", id: "registration_pass_confirm" %>
<div id="passConfirmError">パスワードが一致していません。</div>

ログインバリデーション

完成図
Image from Gyazo

ログインバリデーション Javascriptコード全体
application.js
  // ログインバリデーション
  const loginSubmitCheck = document.querySelector('#login_submit');
  if (loginSubmitCheck) {

    let loginEmailError = false;
    let loginPassError = false;
    let loginError = false;

    document.querySelectorAll('#login_email, #login_pass').forEach(element => {
      element.addEventListener('input', function() {
        if (document.querySelector('#login_email').value.trim() === '' || document.querySelector('#login_pass').value.trim() === '') {
          document.querySelector('#login_submit').classList.add('disabled');
          document.querySelector('#login_submit').setAttribute('disabled', true);
        } else if (loginEmailError || loginPassError || loginError) {
          document.querySelector('#login_submit').classList.add('disabled');
          document.querySelector('#login_submit').setAttribute('disabled', true);
        } else {
          document.querySelector('#login_submit').classList.remove('disabled');
          document.querySelector('#login_submit').removeAttribute('disabled');
        }
      });
    });

    // メールアドレスのinputイベントを監視
    document.querySelector('#login_email').addEventListener('input', function() {
      let email = this.value;
      let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        document.querySelector('#loginEmailError').textContent = 'メールアドレスの形式が正しくありません。';
        document.querySelector('#loginEmailError').style.display = 'block';
        loginEmailError = true;
      } else {
        document.querySelector('#loginEmailError').style.display = 'none';
        loginEmailError = false;
      }
      updateLoginSubmit();
    });

    // パスワードフィールドのinputイベントを監視
    document.querySelector('#login_pass').addEventListener('input', function() {
      if (this.value.length < 6) {
        updateLoginPasswordError();
        document.querySelector('#loginPassError').style.display = 'block';
        loginPassError = true;
      } else {
        document.querySelector('#loginPassError').style.display = 'none';
        loginPassError = false;
      }
      updateLoginSubmit();
    });

    // パスワード最低文字数カウント
    function updateLoginPasswordError() {
      let remaining = 6 - document.querySelector('#login_pass').value.length;
      document.querySelector('#loginRemainingChars').textContent = remaining;
    }

    function updateLoginSubmit() {
      if (document.querySelector('#login_email').value.trim() === '' || document.querySelector('#login_pass').value.trim() === '' || loginEmailError || loginPassError) {
        document.querySelector('#login_submit').classList.add('disabled');
        document.querySelector('#login_submit').setAttribute('disabled', true);
      } else {
        document.querySelector('#login_submit').classList.remove('disabled');
        document.querySelector('#login_submit').removeAttribute('disabled');
      }
    }

    document.addEventListener("ajax:success", function(e){
      let data = e.detail[0];
      if (data.status === true) {
        location.href = "/";
      } else {
        document.querySelector('#loginError').textContent = 'メールアドレスまたはパスワードが間違っています。';
        document.querySelector('#loginError').style.display = 'block';
      }
    });

    document.addEventListener("ajax:error", function(e) {
      document.querySelector('#loginError').textContent = '通信エラーが発生しました。';
      document.querySelector('#loginError').style.display = 'block';
    });

  }

ユーザー名とメールアドレスについては新規登録と同じなので省略します

ログインフォームURLについて

まず前提として、ログインフォームのURLはajaxのデータ送信に使用します
理由はログインボタンをクリックして入力情報が誤っていたときにモーダル上でエラー文を表示できないためです(モーダルでない元のログインページにリダイレクトしてしまいます)

public/sessions/_form.html.erb
<%= form_with model: resource, url: users_check_user_path, local: false, class: 'sessions' do |f| %>
routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'public/registrations',
    sessions: 'public/sessions',
  }
  :
  scope module: :public do
  :
    post 'users/check_user' => 'users#check_user'
  end
  :
end

今回ルーティングがgetでなくpostなのはパスワードがURLに表示されてしまうことを防ぐためです

users_controllre.rb
class Public::UsersController < ApplicationController
  before_action :authenticate_user!, except: [:check_user]
:
  def check_user
    user = User.find_by(email: params[:user][:email])
    if !user.nil? && user.valid_password?(params[:user][:password])
      if user.is_active?
        sign_in user
        flash[:notice] = "ログインしました。"
        render json: {status: true}.to_json
      else
        render json: {status: "inactive"}.to_json
      end
    else
      render json: {status: false}.to_json
    end
  end
:
end

ajaxによって送信されたemailを他のユーザーがすでに登録しているかfine_byで確認します
一致していた場合はsign_in userでログインし、{status: true}を返します
一致しなかった場合は、{status: false}を返します

ログインエラー
application.js
    document.addEventListener("ajax:success", function(e) {
      let data = e.detail[0];
      if (data.status === true) {
        location.href = "/";
      } else if (data.status === false) {
        document.querySelector('#loginError').textContent = 'メールアドレスまたはパスワードが間違っています。';
        document.querySelector('#loginError').style.display = 'block';
      } else if (data.status === "inactive") {
        document.querySelector('#loginError').textContent = '退会済みです。別のメールアドレスをお使いください。';
        document.querySelector('#loginError').style.display = 'block';
      }
    });

{status: true}がtrueであればトップページ(URLが/)に飛び、
{status: false}であればloginError'のidが付与されている箇所に「メールアドレスまたはパスワードが間違っています。」というエラー文が表示されます↓

public/sessions/_form.html.erb
<div id="loginError"></div>

さいごに

最初はjQueryで書いてましたが、最近はjQueryを使わず素のJavascriptで書くことが多くなってきたらしいです!ということで素のJavascriptに書き換えました(ChatGPTでですが💦)
修正点等ございましたらご教示お願いいたします!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?