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?

More than 1 year has passed since last update.

Laravel Sanctum,FortifyでSPAのログイン機能を実装する(バリデーション)

Posted at

はじめに

前回実装したログイン機能に、Vuetifyでバリデーションを実装します。
公式ドキュメントを参考に進めていきます。

前回:https://qiita.com/HuntingRathalos/items/b401ee865c40e94cef46

環境

  • Vue:2.6.14
  • Vuetify:2.6.1

目次

1.ベースとなるコード
2.バリデーション実装
  2-1.各テキストフィールドに制約をつける
  2-2.フォーム全体をバリデーションチェック
3.参考記事

1. ベースとなるコード

下記のコードにバリデーションをかけていきます。

EmailInput.vue
EmailInput.vue
<template>
  <v-text-field
    v-model="setEmail"
    label="メールアドレスを入力"
    placeholder="your@email.com"
    type="email"
    outlined
    dense
    rounded
  />
</template>
<script>
export default {
  props: {
    email: {
      type: String,
      default: ''
    }
  },
  computed: {
    setEmail: {
      get() {
        return this.email
      },
      set(newVal) {
        return this.$emit('update:email', newVal)
      }
    }
  }
}
</script>
PasswordInput.vue
PasswordInput.vue
<template>
  <v-text-field
    v-model="setPassword"
    type="password"
    label="パスワードを入力"
    placeholder="8文字以上"
    outlined
    rounded
    dense
  />
</template>
<script>
export default {
  props: {
    password: {
      type: String,
      default: ''
    }
  },
  computed: {
    setPassword: {
      get() {
        return this.password
      },
      set(newVal) {
        return this.$emit('update:password', newVal)
      }
    }
  }
}
</script>
pages/login.vue
pages/login.vue
<template>
  <v-card max-width="500" class="mx-auto mt-5 full-width" flat outlined>
    <v-card-title class="text-center pa-8">
      <h1 class="text-h5 font-weight-bold full-width">ログイン情報入力</h1>
    </v-card-title>
    <v-divider> </v-divider>
    <div class="px-6 py-8">
      <div style="max-width: 336px" class="mx-auto">
        <v-card-text>
          <v-form @submit.prevent="doLogin">
            <email-input :email.sync="form.email" />
            <password-input :password.sync="form.password" />
            <v-card-actions>
              <v-row justify="end" class="pb-8 pt-4">
                <base-button :color="btnColor">ログイン</base-button>
              </v-row>
            </v-card-actions>
          </v-form>
        </v-card-text>
      </div>
    </div>
  </v-card>
</template>
<script>
import BaseButton from '../../atoms/buttons/BaseButton.vue'
import EmailInput from '../../atoms/inputs/EmailInput.vue'
import PasswordInput from '../../atoms/inputs/PasswordInput.vue'
export default {
  components: { EmailInput, PasswordInput, BaseButton },
  data() {
    return {
      btnColor: 'indigo accent-2',
      form: {
        email: '',
        password: ''
      }
    }
  },
  methods: {
    async doLogin() {
      try {
        await this.$auth.loginWith("laravelSanctum", {
          data: this.form,
        })
		this.$router.push('/')
      } catch (e) {}
    },
  },
}
</script>
<style scoped>
.full-width {
  width: 100%;
}
</style>

2. バリデーション実装

2-1. 各テキストフィールドに制約をつける

Vuetifyのv-text-fieldにバリデーションを設定するには、バリデーション用関数をrules属性に配列で渡します。

今回は、必須入力文字数制限メール形式入力のチェックをする関数を用意しました。

それぞれ下記のようなコードになります。

必須入力
(value) => !!value || '必須項目なので値を入力してください。'
文字数制限
(value) => value.length <= 255 || '文字数をオーバーしています。'
メール形式入力
(value) => {
          const pattern =
            /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
          return pattern.test(value) || '正しい形式で入力してください。'
        }

入力された文字列がチェックを通ればtrueを返し、通らなければエラー文字列を返します。

これらの関数をrules属性に渡すことで、検証することができます。

修正後、最終的なコードは下記のようになりました。

EmailInput.vue(修正後)
EmailInput.vue
<template>
  <v-text-field
    v-model="setEmail"
    :rules="[rules.required, rules.email, rules.maxCount255]" //追加
    label="メールアドレスを入力"
    placeholder="your@email.com"
    type="email"
    outlined
    dense
    rounded
  />
</template>
<script>
export default {
  props: {
    email: {
      type: String,
      default: ''
    }
  },
	//ここから追加
  data() {
    return {
      rules: {
        required: (value) => !!value || '必須項目なので値を入力してください。',
        maxCount255: (value) =>
          value.length <= 255 || '文字数をオーバーしています。',
        email: (value) => {
          const pattern =
            /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
          return pattern.test(value) || '正しい形式で入力してください。'
        }
      }
    }
  },
	//ここまで
  computed: {
    setEmail: {
      get() {
        return this.email
      },
      set(newVal) {
        return this.$emit('update:email', newVal)
      }
    }
  }
}
</script>
PasswordInput.vue(修正後)
PasswordInput.vue
<template>
  <v-text-field
    v-model="setPassword"
    :rules="[rules.required, rules.minCount8]" //追加
    type="password"
    label="パスワードを入力"
    placeholder="8文字以上"
    outlined
    rounded
    dense
  />
</template>
<script>
export default {
  props: {
    password: {
      type: String,
      default: ''
    }
  },
	//ここから追加
  data() {
    return {
      rules: {
        required: (value) => !!value || '必須項目なので値を入力してください。',
        minCount8: (value) =>
          value.length >= 8 || 'パスワードは8文字以上で入力してください。'
      }
    }
  },
	//ここまで
  computed: {
    setPassword: {
      get() {
        return this.password
      },
      set(newVal) {
        return this.$emit('update:password', newVal)
      }
    }
  }
}
</script>

2-2. フォーム全体をバリデーションチェック

現状で、各テキストフィールドに制約をつけることはできたが、不正値の入力があっても送信されてしまうと言う問題点があります。

そのために、コードを修正していきます。

まず、下記のようにv-formref属性を付与してVue側からアクセスできるようにします。

<v-form ref="login_form">
//略
</v-form>

v-formrefを設定することで、validate()にアクセスできるようになっています。
validate()は、すべての要素がバリデーションを通過しているかを確認してくれるので、バリデーションをかけたいタイミングで呼びます。

修正後、最終的なコードは下記のようになりました。

pages/login.vue
pages/login.vue
<template>
  <v-card max-width="500" class="mx-auto mt-5 full-width" flat outlined>
    <v-card-title class="text-center pa-8">
      <h1 class="text-h5 font-weight-bold full-width">ログイン情報入力</h1>
    </v-card-title>
    <v-divider> </v-divider>
    <div class="px-6 py-8">
      <div style="max-width: 336px" class="mx-auto">
        <v-card-text>
          <v-form ref="login_form" @submit.prevent="doLogin"> //ref属性で名前をつける
            <email-input :email.sync="form.email" />
            <password-input :password.sync="form.password" />
            <v-card-actions>
              <v-row justify="end" class="pb-8 pt-4">
                <base-button :color="btnColor">ログイン</base-button>
              </v-row>
            </v-card-actions>
          </v-form>
        </v-card-text>
      </div>
    </div>
  </v-card>
</template>
<script>
import BaseButton from '../../atoms/buttons/BaseButton.vue'
import EmailInput from '../../atoms/inputs/EmailInput.vue'
import PasswordInput from '../../atoms/inputs/PasswordInput.vue'
export default {
  components: { EmailInput, PasswordInput, BaseButton },
  data() {
    return {
      btnColor: 'indigo accent-2',
      form: {
        email: '',
        password: ''
      }
    }
  },
  methods: {
    async doLogin() {
      if (this.$refs.login_form.validate()) { //追加
        try {
          await this.$auth.loginWith('laravelSanctum', {
            data: this.form
          })
          this.$router.push('/')
        } catch {}
      } //追加
    }
  }
}
</script>
<style scoped>
.full-width {
  width: 100%;
}
</style>

以上で実装完了となります。

3. 参考記事

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?