CODEX

How to use vee-validate with bootstrap-vue and TypeScript.

Ruhshan Ahmed Abir
CodeX
Published in
7 min readMar 5, 2021

--

Photo by Sigmund on Unsplash

In a web app forms are the most easy and well known way of taking input from user. But with forms, comes validation. There are many ways of incorporating validation with forms. Here we will see how to use vee-validate in a VueJS project where bootsrap-vue is used as ui framework.

First of all let’s create a layout of a form containing 3 fields: name, email, phone number.

<b-form>
<b-form-group>
<b-form-input
placeholder="Enter Name"
required
type="text"
></b-form-input>
</b-form-group>
<b-form-group
label="Email address:"
>
<b-form-input
placeholder="Enter email"
required
type="email"
></b-form-input>
</b-form-group>
<b-form-group
label="Phone Number:"
>
<b-form-input
placeholder="Enter Phone Number"
required
type="text"
></b-form-input>
</b-form-group>
<b-button variant="success">Submit</b-button>
</b-form>

Pretty basic right? Instead of using pure html tags, divs and bootstrap specific classes we are here utilizing components provided by bootstrap-vue to achieve our goal. After running with npm run serve it should look like this:

Initial look of the form

Now we will add validation one by one. Let’s start with the name field. To enable validation we will wrap the b-form-group component with validation provider like this:

<validation-provider v-slot="{ errors, valid }" name="Name" rules="required">
<b-form-group
label="Name: "
>
<b-form-input
v-model="name"
placeholder="Enter Name"
required
type="text"
></b-form-input>
</b-form-group>
</validation-provider>

Here, validation-provider is wrapped around the input and provides validation using scoped slots. There are many scoped slot props but for this example we opted only errors and valid. Former one contains a list off error messages and later one is a flag denoting the input is valid or not. using name attribute we specified the name of the field, which will be rendered in error messages and using rules we specified which should be applied during validation. For start we only one rule which is required.

But, this template is not complete yet. We haven’t added any place to display the error messages. To do that we have to add the following code within the scope of validation-provider:

<b-form-invalid-feedback :state="valid">
<span v-for="(error, index) in errors" :key="index">{{ error }}</span>
</b-form-invalid-feedback>

This component will be displayed based on the value of valid as we’ve bound it with b-form-invalid-feedback component’s state prop. So the full template now looks like this:

<b-form-group
label="Name: "
>
<b-form-input
v-model="name"
placeholder="Enter Name"
required
type="text"
></b-form-input>
<b-form-invalid-feedback :state="valid">
<span v-for="(error, index) in errors" :key="index">{{ error }}</span>
</b-form-invalid-feedback>
</b-form-group>

We are done with templating. Now just a few lines in the script and we are good. Currently this is what the script looks like:

<script lang="ts">

import { Component, Vue } from 'vue-property-decorator'

import { extend, setInteractionMode, ValidationProvider } from 'vee-validate'
import { required } from 'vee-validate/dist/rules'

setInteractionMode('eager')

extend('required', {
...required,
message: '{_field_} can not be empty'
})

@Component({ components: { ValidationProvider } })
export default class SampleForm extends Vue {
private name = ''
}

</script>

Let’s see how it looks:

required rule in action

If we want to change the error message, it’s very easy. Just change the message attribute in the chunk where we extended the required rule like this:

extend('required', {
...required,
message: 'You may not left {_field_} empty'
})

Now it should look like this:

Changed error message

Let’s add another validation. We want to make sure name is not more than 40 characters. For this we will import the rule:

import { required, max } from 'vee-validate/dist/rules'

And then extend with a desired error message:

extend('max', {
...max,
message: '{_field_} should not exceed {length} characters'
})

Then we add the rule in the template:

<validation-provider v-slot="{ errors, valid }" name="Name" rules="required|max:20">

Here we specified the length parameter for the rule max which is 20. Now if we enter more than 20 characters, it will look like this:

Enabled character length validation for Name

Now let’s add validation for the email field. First we import the rule and extend it:


import { required, max, email } from 'vee-validate/dist/rules'
/extend the rule
extend('email', {
...email,
message: '{_value_} is not a valid email'
})

Now we’ll update the template. We will wrap the email input with validation-provider and insert necessary codes for validation rules and rendering error messages:

<validation-provider v-slot="{ errors, valid }" name="Email" rules="required|email">
<b-form-group
label="Email address:"
>
<b-form-input
placeholder="Enter email"
v-model="email"
required
type="email"
></b-form-input>
<b-form-invalid-feedback :state="valid">
<span v-for="(error, index) in errors" :key="index">{{ error }}</span>
</b-form-invalid-feedback>
</b-form-group>
</validation-provider>

Now if we enter invalid email it will look like this:

Invalid email showing error

Vee-valdiate provides many validation rules which will suffice almost all of your validation cases. But sometimes you may need to write your own validation rule. In that case you may use regex. For example, in our phone number field we want it to follow this pattern: 9 digits followed by 01 at start. In regex expression it looks like this: /^(01)([0-9]{9})$/ . To plug this, first we import the rule and extend this with a proper message:

import { required, max, email, regex as phoneNumber } from 'vee-validate/dist/rules'

There might come a case where you want same rule to validate multiple field but with different error messages. In that case you can use typescript’s type assertion to import a rule with different names. Just like we imported regex here as phoneNumber. Let’s extend this:

extend('phoneNumber', {
...phoneNumber,
message: 'Please enter a valid phone number'
})

Now the regex for this needs to be inserted in the template. Template for phone number field with validation provider looks like this:

<validation-provider v-slot="{ errors, valid }" name="Phone Number" :rules="{required: true, phoneNumber: /^(01)([0-9]{9})$/ }">
<b-form-group
label="Phone Number:"
>
<b-form-input
placeholder="Enter Phone Number"
v-model="phone"
required
type="text"
></b-form-input>
<b-form-invalid-feedback :state="valid">
<span v-for="(error, index) in errors" :key="index">{{ error }}</span>
</b-form-invalid-feedback>
</b-form-group>
</validation-provider>

Noticed that, for this case we define the rules a bit differently. Instead of specified the rules as pipe(‘|’) separated strings, we’ve passed a object. This is how vee-validated suggests for safety. Because your regex might contain pipe itself which may produce unexpected result.

Error message being shown for invalid phone number

We have to add a last piece of the puzzle. Notice that though we are entering invalid inputs, submit buttons remain enabled. We want to disable it if one or more field contains invalid input. To do this we need to wrap the whole form withing another component called validation-observer. At first let’s import this in the script and plug in the components section.

import { extend, setInteractionMode, ValidationProvider, ValidationObserver } from 'vee-validate'/** other imports and declaration@Component({ components: { ValidationProvider, ValidationObserver } })
export default class SampleForm extends Vue {
private name = ''
private email = ''
private phone = ''
}

Now let’s wrap the form inside validation observer

<validation-observer v-slot="{ invalid }">
<b-form>*** other fields **</b-form>
</validation-observer>

Like validation-provider we have opted out the invalid prop from valdiation-observer. Now we will bind this with the submit button so that it stays deactivated if one or more fields are invalid.

<b-button variant="success" :disabled="invalid">Submit</b-button>
Submit button inactive
Submit button active when all fields are valid

Now the whole vue component looks like this:

If you want to take a look at the whole project here it is:

https://github.com/Ruhshan/vee-validate-bootstrap-ts

Feel free to let me know if you find any mistake or have any suggestion for me. Happy coding.

--

--

Ruhshan Ahmed Abir
CodeX

Started with poetry, ended up with codes. Have a university degree on Biotechnology. Works and talks about Java, Python, JS. www.buymeacoffee.com/ruhshan