Web備忘録

雑多に書いています

Vueでパスワードの強度を動的に判定できる送信フォームを作りました。

タイトルの通り、Vueでパスワードの強度をクライアントサイドで判定しながら送信できるフォームを作りました。

f:id:fujiten3:20190711142645g:plain
PasswordStorengthChecker

JSFiddle上に置いておいたので、欲しい方はどうぞ。編集自由ですが、ブログ等での再配布はご遠慮ください。

jsfiddle.net

挙動については本当にgifで見てもらった通りなのですが、4つの条件(文字数、小文字、数字、大文字)を満たしたパスワードのみ送信を行うというものです。

多すぎる条件はユーザー体験を損ないかねないので、4つも条件が必要かは微妙なところですが、セキュリティに関するところというところもあり、判定が甘すぎるよりはいいと思います。

以下、コードの解説です。Vueに興味のない人は最後の余談だけでもよければ御覧ください。(大したことは話してません)

HTML側

<div id="app">
  <div class="container">

    <div class="input_container">
      <label for="name" class="label">パスワード</label>
      <input type="password" @input="onCheckedBeValid" v-model="password" placeholder="6文字以上" />
      <span v-bind:class="{ valid_password_length: validPasswordLength }" class="password_length">{{passwordLength}}</span>
    </div>
    
    <div class="validation_container">
      <p v-bind:class="{ lowercase_valid: containsLowercase }">小文字</p>
      <p v-bind:class="{ number_valid: containsNumber }">数字</p>
      <p v-bind:class="{ uppercase_valid: containsUppercase }">大文字</p>
    </div>

    <div class="button_container">
      <button @click="signUp">登録</button>
      <p>{{ message }}</p>
    </div>

  </div>
</div>

inputタグについて

<input type="password" @input="onCheckedBeValid" v-model="password" placeholder="6文字以上" />

@inputで「inputイベント」が起こるたびに着火されるメソッドを指定し、入力された値(value)については、v-model="password"を使って、双方向バインディングしています。

spanタグについて

<span v-bind:class="{ valid_password_length: validPasswordLength }" class="password_length">{{passwordLength}}</span>

属性の値を動的にセットできるv-bindを使い、今回はclass属性の値を動的にセットしています。

「v-bind:class="{ valid_password_length: validPasswordLength }"」というのは、validPasswordLengthがtrueのとき、valid_password_lengthというクラスをこのタグにセットするという意味になります。これを使い、文字数が条件を満たしたときだけに、動的にクラス名をセットして、付与されるスタイルを変更しています。

JS(Vue)側

new Vue({
  el: "#app",
  data () {
    return {
      password: '',
      message: '',
      passwordLength: 0,
      containsLowercase: false,
      containsNumber: false,
      containsUppercase: false,
      validPasswordLength: false,
      isValidPassword: false
    }
  },
  methods: {
    onCheckedBeValid () {
      this.passwordLength = this.password.length

      this.passwordLength > 5 ? this.validPasswordLength = true : this.validPasswordLength = false
      this.containsLowercase = /[a-z]/.test(this.password)
      this.containsNumber = /\d/.test(this.password)
      this.containsUppercase = /[A-Z]/.test(this.password)
    },
    checkAllValidations () {
      if (this.containsLowercase 
          && this.containsNumber 
          && this.containsUppercase
          && this.validPasswordLength) {
        this.isValidPassword = true 
      } else {
        this.isValidPassword = false
      }
    },
    signUp () {
      this.checkAllValidations()
      this.isValidPassword ? this.message = '送信しました' : this.message = 'パスワードに問題があります'
    }
  }
})

メソッドの簡単な説明を残しておきます。

onCheckedBeValidメソッド

Inputイベントで着火するメソッドで、入力されたパスワードが先に述べた4つの条件を満たすかどうかそれぞれ判定します。ここでtrueになった条件たちは、HTML側で:classにバインドされているので、それぞれスタイル付与に貢献します。

checkAllValidationsメソッド

メソッド名通り、全てのバリデーションをチェックします。

signUpメソッド

ここにバックエンドにアクセスするための非同期通信の処理を書きます。axios等を使いましょう。ここでは記述を割愛しています。

CSS

.container {
  margin: 30px auto;
  height: auto;
}

.input_container, .validation_container, .button_container {
  display: block;
  margin: 0 auto;
  height: auto;
  display: flex;
  justify-content: center;
}

.password_length {
  padding: 2px 10px;
  margin-left: 120px; 
  background: rgb(236, 57, 57);
  color: white;
  border-radius: 10px;
  font-size: 13px;
  transition: all .1s;
  position: absolute;
}

.valid_password_length {
  background: green;
}

.validation_container p {
  width: 80px;
  margin: 5px;
  height: auto;
  font-size: 12px;
  text-align: center;
  border-radius: 2px;
  color: rgba(71, 87, 98, .8);
  background: linear-gradient(to right, green 50%, #eee 50%);
  background-color: #eee;
  background-size: 200% 100%;
  background-position: right;
  transition: background .3s;
}

.lowercase_valid,
.number_valid,
.uppercase_valid {
  background-position: left !important;
  color: white !important;
}

p {
  margin: 0;
}

一番のポイントは緑色のバーが動的に動くところだと思うので、そこの部分だけ抜粋します。

 .validation_container p {
  background: linear-gradient(to right, green 50%, #eee 50%);
  background-color: #eee;
  background-size: 200% 100%;
  background-position: right;
  transition: background .3s;
}

.lowercase_valid,
.number_valid,
.uppercase_valid {
  background-position: left !important;
  color: white !important;
}

こちらがその部分です。文章だけで解説しますが、まず、2行目のbackground: linear-gradient(to right, green 50%, #eee 50%);で「左50%が緑、右側50%が白色の背景」をセットしています。そして、background-position: right;で、「その背景の右側(白色)を自分のポジション」として固定します。

次に、「パスワードが条件を満たしたときのクラスの追加」で、background-position: left !important;というスタイルを付与することで、今まで右側(白色)にいた自分が、左側(緑色)に移ります。その移動をトランジション要素で動的に行うことで、白色と緑色の境界線が、まるでバーの動きのように表現されているというわけです。

コンポーネント・モジュール化

新規登録画面が2ページ以上ある場合は、このフォームをコンポーネント化しておいた方がいいと思われますが、2ページ以上あるサイトはあまりないと思われますので、必要になるまでは共通化しなくてもいい気がします。自分は共通化せずにそのまま使ってます。

「条件を満たすと満タンになるバー」は、他の部分で再利用できそうなので、そこはコンポーネントにしてもいいかもしれまん。

余談

4つの条件をわざわざ満たさせるフォームはユーザにとって煩わしさはあるかもしれませんが、認証情報はサイトの核なので、特に「決済システム」などの重大な機能をもつサイトを運用する場合は、このようなフォームでパスワード強度を動的に確認してあげるとよいと思います。

バックエンドに渡す前にバリデーションにかけれるので、無駄な通信も防げてエコですしね。(だいぶ微々たるものでしょうけど)

おしまい。