Vuelidate example

by pandigity

HTML

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuelidate.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/validators.min.js"></script>
<div id="vue">
  <form @submit.prevent="submit">
    <h1>Register</h1>
    <v-field label="Username":v="$v.username">
      <input v-model="username" @blur="$v.username.$touch()">
    </v-field>
    <v-field label="Email" :v="$v.email">
      <input v-model="email" @blur="$v.email.$touch()">
    </v-field>
    <v-field label="Password" :v="$v.password" type="password">
      <input v-model="password" type="password" @blur="$v.password.$touch()">
      <template slot="note">
        Must be at least 8 characters and contain a lowercase character, uppercase character and a number.
      </template>
    </v-field>
    <v-field label="Repeat password" :v="$v.password2">
      <input v-model="password2" type="password" @blur="$v.password2.$touch()">
    </v-field>
    <button type="submit" :class="{invalid:$v.$invalid}">Submit</button>
    <button type="button" @click="reset">Reset</button>
  </form>
  <pre>{{$data}}</pre>
  <pre>{{$v}}</pre>
</div>

SCSS

form{
  padding:30px;
  width:200px;
  border:1px solid lightgrey;
}
h1{
  margin-top:0px;
}
input{
  display:block;
  width:100%;
  box-sizing:border-box;
  border:1px solid lightgrey;
  padding:5px;
}
button{
  padding:5px 15px;
}
.field{
  margin-bottom:10px;
  &.required label::after{
    content:'*';
    color:red;
  }
  &.valid{
    input{
      border-color:green;
    }
    label::after{
      color:green;
    }
  }
  &.error{
    color:red;
    input{
      border-color:red;
    }
  }
  .note{
    font-size:12px;
  }
}
button.invalid{
  opacity:0.5;
}

JavaScript

Vue.use(window.vuelidate.default)

//This is a reusable component for showing validation state and error messages for each field
Vue.component('v-field', {
	template:`
  <div class="field" :class="{error:v.$error, valid:!v.$invalid, required:v.$params.required}">
  	<label>{{label}}</label>
    <slot></slot>
    <div class="note">
    	<slot name="note"></slot>
    </div>
    <div>{{message}}</div>
  </div>`,
  props:['label', 'v'],
  computed:{
  	message () {
      if (this.v.$error) {
        for (let key in this.$options.messages) {
          if (this.v[key] === false) {
            return this.$options.messages[key](this.v)
          }
        }
      } else {
      	return null
      }
    },
    model:{
    	get(){
      	return this.value
      },
      set(val){
      	this.$emit('input',val)
      }
    }
  },
  messages:{ //Add more messages here when you need, the key should be the same as the name of the validator
    required: v => 'This field is required',
    email: v => 'Must be a valid e-mail',
    alphaNum: v => 'May only contain letters and numbers',
    minLength: v => 'Must be at least ' + v.$params.minLength.min + ' characters',
    goodPassword: v => '', //The password rules are always displayed, so no need to show a message
    sameAs: v => 'Must be same as ' + v.$params.sameAs.eq
  }
})
//$invalid is true when the field is invalid. $error is true when the field is invalid AND it has been $touched. This is useful so that we don't immediately show all fields as invalid before the user has had a chance to fill them out.


//And now we initialize Vue and the form
new Vue({
	el:'#vue',
  data(){
  	return {
    	username:'',
      email:'',
      password:'',
      password2:''
    }
  },
  methods:{
  	submit(){
    	this.$v.$touch()
    	if(this.$v.$invalid){
      	alert('All fields are not valid')
      } else {
      	alert('Success!')
      }
    },
    reset(){
    	Object.assign(this.$data, this.$options.data())
     ...