Let’s make 3 examples of validation date and time in Vue 3 using the Vue.js model validation library Vuelidate. In the last example, we will use the Moment.js package.
I used to think that validating dates in javascript is difficult, but after I learned Vuelidate and Moment.js my thoughts changed for the better. so I wanted to make this short tutorial for helping other developers.
We will create 3 examples the first example is for comparing and validating dates, The second example is for date-time validation, and the last one is for validating date and time in minutes so we will use moment.js for a better experience and easy coding. Let’s get started
To install the packages use
npm install @vuelidate/core @vuelidate/validators
// OR
yarn add @vuelidate/core @vuelidate/validators
npm install moment --save
// OR
yarn add moment
Vuelidate Date Validation
Let’s say we want to make a reservation, so we will have a start date input and end date input and we should make sure that the start date after today and the end date after the start date.
here is the <template>
and <script>
<template>
<section class="h-100 h-custom bg-light" >
<div class="container py-5 h-100">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col d-flex justify-content-center align-items-center">
<div class="card border-0 " style="min-width:350px; max-width:500px">
<div class="card-body">
<h3 class="mb-4">Reservation</h3>
<hr/>
<div v-if="v$.$errors.length > 0" class="alert alert-danger" role="alert">
<ul>
<li
v-for="error of v$.$errors"
:key="error.$uid"
>
<strong>{{ error.$validator }}</strong>
<small> on property </small>
<strong>{{ error.$property }}</strong>
<small> says: </small>
<strong>{{ error.$message }}</strong>
</li>
</ul>
</div>
<form @submit="onSubmit">
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">Start date</label>
<input type="date" step="1" class="form-control"
v-model.trim="form.startDate"
@input="setTouched('startDate')"
:class="v$.form.startDate.$error?'is-invalid':''"
>
<div v-for="error of v$.form.startDate.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">End Date</label>
<input type="date" class="form-control"
v-model.trim="form.endDate"
@input="setTouched('endDate')"
:class="v$.form.endDate.$error?'is-invalid':''"
>
<div v-for="error of v$.form.endDate.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<hr/>
<hr/>
<div class="mb-3 text-center">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
<hr/>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
import useVuelidate from '@vuelidate/core'
import { required, helpers} from '@vuelidate/validators'
import moment from 'moment';
export default {
name: 'Registration',
setup() {
return { v$: useVuelidate() }
},
data() {
return {
form: {
startDate:null,
endDate:null,
},
}
},
validations() {
return {
form: {
startDate: {
required,
minValue: helpers.withMessage('End date must be after today', value => {
console.log(value)
return new Date(value) > new Date()
}),
},
endDate: {
required,
minValue: helpers.withMessage('End date must be after the start date', value => {
console.log(value)
return new Date(value) > new Date(this.form.startDate)
}),
},
}
},
methods: {
setTouched(theModel) {
if(theModel == 'startDate' || theModel == 'all'){this.v$.form.startDate.$touch()}
if(theModel == 'endDate' || theModel == 'all'){this.v$.form.endDate.$touch()}
},
async onSubmit(event) {
event.preventDefault()
this.setTouched('all');
if (!this.v$.$invalid)
{
alert('all Good')
}
},
},
}
</script>
Vuelidate Date Time Validation
Let’s create the same example above but we will add time (Hours, Minutes, Seconds) to the validation comparison.
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">Start Date Time</label>
<input type="datetime-local" class="form-control"
v-model.trim="form.startDateTime"
@input="setTouched('startDateTime')"
:class="v$.form.startDateTime.$error?'is-invalid':''"
>
<div v-for="error of v$.form.startDateTime.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">End Date Time</label>
<input type="datetime-local" class="form-control"
v-model.trim="form.endDateTime"
@input="setTouched('endDateTime')"
:class="v$.form.endDateTime.$error?'is-invalid':''"
>
<div v-for="error of v$.form.endDateTime.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
data() {
return {
form: {
startDateTime:null,
endDateTime:null,
},
}
},
validations() {
return {
form: {
startDateTime: {
required,
minValue: helpers.withMessage('Start date time must be after today time', value => {
console.log(value)
return new Date(value) > new Date()
}),
},
endDateTime: {
required,
minValue: helpers.withMessage('End date time must be after the start date time', value => {
console.log(value)
return new Date(value) > new Date(this.form.startDateTime)
}),
},
},
}
}
},
methods: {
setTouched(theModel) {
if(theModel == 'startDateTime' || theModel == 'all'){this.v$.form.startDateTime.$touch()}
if(theModel == 'endDateTime' || theModel == 'all'){this.v$.form.endDateTime.$touch()}
},
Vuelidate Date and Time Validation With Moment.js
My Favorite example so far. Let’s make the start date-time must be after 30 minutes from now as a minimum value and the end date time must be after the start date time as a maximum value of 120 minutes and a minimum of 45 minutes from the start date.
<script>
import useVuelidate from '@vuelidate/core'
import { required, helpers} from '@vuelidate/validators'
import moment from 'moment';
export default {
name: 'Registration',
setup() {
return { v$: useVuelidate() }
},
data() {
return {
form: {
startDateTimeIn30:null,
endDateTimeIn30:null,
},
}
},
validations() {
return {
form: {
startDateTimeIn30: {
required,
minValue: helpers.withMessage('Start date time must be after 30 minutes form now', value => {
let valueToCompare = moment().add(30, 'm').format()
return moment(value).isAfter(valueToCompare)
}),
},
endDateTimeIn30: {
required,
minValue: helpers.withMessage('End date time must be after the start date time max 120 minutes, min 45 minutes from start date', value => {
let valueToCompareStart = moment().add(75, 'm').format() // 30 min start date + 45 min end date
let valueToCompareEnd = moment().add(150, 'm').format() // 30 min start date + 120 min end date
return moment(value).isBetween(valueToCompareStart,valueToCompareEnd)
}),
},
},
}
},
methods: {
setTouched(theModel) {
if(theModel == 'startDateTimeIn30' || theModel == 'all'){this.v$.form.startDateTimeIn30.$touch()}
if(theModel == 'endDateTimeIn30' || theModel == 'all'){this.v$.form.endDateTimeIn30.$touch()}
},
async onSubmit(event) {
event.preventDefault()
this.setTouched('all');
if (!this.v$.$invalid)
{
alert('all Good')
}
},
},
}
</script>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">Start Date Time Min 30 minutes from now</label>
<input type="datetime-local" class="form-control"
v-model.trim="form.startDateTimeIn30"
@input="setTouched('startDateTimeIn30')"
:class="v$.form.startDateTimeIn30.$error?'is-invalid':''"
>
<div v-for="error of v$.form.startDateTimeIn30.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">End Date Time Max 2 Hours Mim 45 Minutes Form </label>
<input type="datetime-local" class="form-control"
v-model.trim="form.endDateTimeIn30"
@input="setTouched('endDateTimeIn30')"
:class="v$.form.endDateTimeIn30.$error?'is-invalid':''"
>
<div v-for="error of v$.form.endDateTimeIn30.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
All Vuelidate Examples In One Component
<template>
<section class="h-100 h-custom bg-light" >
<div class="container py-5 h-100">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col d-flex justify-content-center align-items-center">
<div class="card border-0 " style="min-width:350px; max-width:500px">
<div class="card-body">
<h3 class="mb-4">Reservation</h3>
<hr/>
<div v-if="v$.$errors.length > 0" class="alert alert-danger" role="alert">
<ul>
<li
v-for="error of v$.$errors"
:key="error.$uid"
>
<strong>{{ error.$validator }}</strong>
<small> on property </small>
<strong>{{ error.$property }}</strong>
<small> says: </small>
<strong>{{ error.$message }}</strong>
</li>
</ul>
</div>
<form @submit="onSubmit">
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">Start date</label>
<input type="date" step="1" class="form-control"
v-model.trim="form.startDate"
@input="setTouched('startDate')"
:class="v$.form.startDate.$error?'is-invalid':''"
>
<div v-for="error of v$.form.startDate.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">End Date</label>
<input type="date" class="form-control"
v-model.trim="form.endDate"
@input="setTouched('endDate')"
:class="v$.form.endDate.$error?'is-invalid':''"
>
<div v-for="error of v$.form.endDate.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<hr/>
<hr/>
<hr/>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">Start Date Time</label>
<input type="datetime-local" class="form-control"
v-model.trim="form.startDateTime"
@input="setTouched('startDateTime')"
:class="v$.form.startDateTime.$error?'is-invalid':''"
>
<div v-for="error of v$.form.startDateTime.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">End Date Time</label>
<input type="datetime-local" class="form-control"
v-model.trim="form.endDateTime"
@input="setTouched('endDateTime')"
:class="v$.form.endDateTime.$error?'is-invalid':''"
>
<div v-for="error of v$.form.endDateTime.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<hr/>
<hr/>
<hr/>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">Start Date Time Min 30 minutes from now</label>
<input type="datetime-local" class="form-control"
v-model.trim="form.startDateTimeIn30"
@input="setTouched('startDateTimeIn30')"
:class="v$.form.startDateTimeIn30.$error?'is-invalid':''"
>
<div v-for="error of v$.form.startDateTimeIn30.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<!-- Input--------------------->
<div class="mb-3">
<label class="form-label">End Date Time Max 2 Hours Mim 45 Minutes Form </label>
<input type="datetime-local" class="form-control"
v-model.trim="form.endDateTimeIn30"
@input="setTouched('endDateTimeIn30')"
:class="v$.form.endDateTimeIn30.$error?'is-invalid':''"
>
<div v-for="error of v$.form.endDateTimeIn30.$errors" class="invalid-feedback" :key="error.$uid">
{{ error.$message }}
</div>
</div>
<hr/>
<hr/>
<div class="mb-3 text-center">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
<hr/>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
import useVuelidate from '@vuelidate/core'
import { required, helpers} from '@vuelidate/validators'
import moment from 'moment';
export default {
name: 'Registration',
setup() {
return { v$: useVuelidate() }
},
data() {
return {
form: {
startDate:null,
endDate:null,
startDateTime:null,
endDateTime:null,
startDateTimeIn30:null,
endDateTimeIn30:null,
},
}
},
validations() {
return {
form: {
startDate: {
required,
minValue: helpers.withMessage('End date must be after today', value => {
console.log(value)
return new Date(value) > new Date()
}),
},
endDate: {
required,
minValue: helpers.withMessage('End date must be after the start date', value => {
console.log(value)
return new Date(value) > new Date(this.form.startDate)
}),
},
startDateTime: {
required,
minValue: helpers.withMessage('Start date time must be after today time', value => {
console.log(value)
return new Date(value) > new Date()
}),
},
endDateTime: {
required,
minValue: helpers.withMessage('End date time must be after the start date time', value => {
console.log(value)
return new Date(value) > new Date(this.form.startDateTime)
}),
},
startDateTimeIn30: {
required,
minValue: helpers.withMessage('Start date time must be after 30 minutes form now', value => {
let valueToCompare = moment().add(30, 'm').format()
return moment(value).isAfter(valueToCompare)
}),
},
endDateTimeIn30: {
required,
minValue: helpers.withMessage('End date time must be after the start date time max 120 minutes, min 45 minutes from start date', value => {
let valueToCompareStart = moment().add(75, 'm').format() // 30 min start date + 45 min end date
let valueToCompareEnd = moment().add(150, 'm').format() // 30 min start date + 120 min end date
return moment(value).isBetween(valueToCompareStart,valueToCompareEnd)
}),
},
},
}
},
methods: {
setTouched(theModel) {
if(theModel == 'startDate' || theModel == 'all'){this.v$.form.startDate.$touch()}
if(theModel == 'endDate' || theModel == 'all'){this.v$.form.endDate.$touch()}
if(theModel == 'startDateTime' || theModel == 'all'){this.v$.form.startDateTime.$touch()}
if(theModel == 'endDateTime' || theModel == 'all'){this.v$.form.endDateTime.$touch()}
if(theModel == 'startDateTimeIn30' || theModel == 'all'){this.v$.form.startDateTimeIn30.$touch()}
if(theModel == 'endDateTimeIn30' || theModel == 'all'){this.v$.form.endDateTimeIn30.$touch()}
},
async onSubmit(event) {
event.preventDefault()
this.setTouched('all');
if (!this.v$.$invalid)
{
alert('all Good')
}
},
},
}
</script>
That’s all, I hope that was useful for you, and if you want to learn more about Vuelidate Vue3 validation, here is a tutorial: Vue 3 Form Validation Example With Explanation