Search code examples
vuejs2vuetify.jsvuelidate

Vuejs: vuelidate conditional validation


suppose we have a password field and we want to do some validations of length, specialcharacters, numbers, upper case and lower case etc, only if the field has a value

how can we do that with vuelidate.

here I am import vuelidate

import { required, minLength, maxLength, email, sameAs } from 'vuelidate/lib/validators'

Here the validations

validations: {
    editedItem: {
      firstname: { required, minLength: minLength(3), maxLength: maxLength(20) },
      lastname: { required, minLength: minLength(3), maxLength: maxLength(20) },
      email: { required, email },
      password: {
        required,
        minLength: minLength(6),
        maxLength: maxLength(15),
        oneNumber,
        oneUpperCase,
        oneLowerCase,
      },
      repassword: {
        sameAsPassword: sameAs('password'),
      },
    },
  }

,

oneNumber, oneUpperCase and oneLowerCase are custom validations:

const oneNumber = (value) => /[0-9]/.test(value)
const oneUpperCase = (value) => /[A-Z]/.test(value)
const oneLowerCase = (value) => /[a-z]/.test(value)

I will forever appreciate any help or advice


Solution

  • My solution involves a separate "custom validators" file.

    validators.js:

    import { helpers as vuelidateHelpers } from 'vuelidate/lib/validators'
    
    export const oneUppercase = value => {
      if (!vuelidateHelpers.req(value)) {
        return true
      }
      const match = value.match(/[A-Z]/g) || []
      return match.length >= 1
    }
    export const oneLowercase = value => {
      if (!vuelidateHelpers.req(value)) {
        return true
      }
      const match = value.match(/[a-z]/g) || []
      return match.length >= 1
    }
    export const oneSpecial = value => {
      if (!vuelidateHelpers.req(value)) {
        return true
      }
      const match = value.match(/[ !@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/g) || []
      return match.length >= 1
    }
    export const oneNumber = value => {
      if (!vuelidateHelpers.req(value)) {
        return true
      }
      const match = value.match(/\d/g) || []
      return match.length >= 1
    }
    

    In the component, with visual formatting to let the user know if their password meets the requirement:

    <template>
      <form>
        <label for="password">Password</label>
        <input
                id="password"
                name="password"
                type="password"
                v-model="form.password"
                @blur="$v.form.password.$touch()"
                @input="validatePassword"
        />
        <label for="confirmPassword">Confirm Password</label>
        <input
                id="confirmPassword"
                name="confirmPassword"
                type="password"
                v-model="form.confirmPassword"
                @blur="$v.form.confirmPassword.$touch()"
        />
        <ul>
          <li>
              {{
              passwordValidations.eightCharacters ?
              '✓':
              '╳'
              }}
            <span class="ml-1">MUST contain 8 characters</span>
          </li>
          <li>
              {{
              passwordValidations.oneUppercase ?
              '✓':
              '╳'
              }}
            <span class="ml-1">MUST contain one uppercase letter</span>
          </li>
          <li>
              {{
              passwordValidations.oneLowercase ?
              '✓':
              '╳'
              }}
            <span class="ml-1">MUST contain one lowercase letter</span>
          </li>
          <li>
              {{
              passwordValidations.oneNumber ?
              '✓':
              '╳'
              }}
            <span class="ml-1">MUST contain one number</span>
          </li>
          <li>
              {{
              passwordValidations.oneSpecial ?
              '✓':
              '╳'
              }}
            <span class="ml-1">MUST contain one special character</span>
          </li>
        </ul>
      </form>
    </template>
    
    <script>
      import {
        required,
        minLength,
        sameAs,
      } from 'vuelidate/lib/validators'
      import {
        oneNumber,
        oneSpecial,
        oneUppercase,
        oneLowercase,
      } from '../validators'
    
      export default {
        data () {
          return {
            form: {
              password: '',
              confirmPassword: '',
            },
            passwordValidations: {
              eightCharacters: false,
              oneUppercase: false,
              oneLowercase: false,
              oneNumber: false,
              oneSpecial: false,
            },
          }
        },
        computed: {
          passwordErrors () {
            const errors = []
            if (!this.$v.form.password.$dirty) return errors
            !this.$v.form.password.required && errors.push('Password is required.')
            return errors
          },
          confirmPasswordErrors () {
            const errors = []
            if (!this.$v.form.confirmPassword.$dirty) return errors
            !this.$v.form.confirmPassword.required && errors.push('Please confirm your password.')
            !this.$v.form.confirmPassword.sameAsPassword && errors.push('Passwords don\'t match')
            return errors
          },
        },
        methods: {
          validatePassword () {
            this.passwordValidations.eightCharacters = this.$v.form.password.eightCharacters
            this.passwordValidations.oneUppercase = this.$v.form.password.oneUppercase
            this.passwordValidations.oneLowercase = this.$v.form.password.oneLowercase
            this.passwordValidations.oneNumber = this.$v.form.password.oneNumber
            this.passwordValidations.oneSpecial = this.$v.form.password.oneSpecial
          },
        },
        validations: {
          form: {
            password: {
              required,
              minLength: minLength(8),
              oneUppercase,
              oneSpecial,
              oneNumber,
              oneLowercase,
            },
            confirmPassword: {
              required,
              sameAsPassword: sameAs('password'),
            },
          },
        },
      }
    </script>