In this post we'll look at how to add a VeeValidate rule for validating a value with an API endpoint. A scenario where such a rule is useful could be checking if a username is unique.
Rather than displaying an error after the form is submitted, we'd like to inform the user right away that their username is already taken.
We'll start with this simple component using a ValidationObserver
and ValidationProvider
.
<template>
<ValidationObserver v-slot="{ invalid }">
<ValidationProvider name="username" rules="required" v-slot="{ errors }">
<p>
<input placeholder="username" v-model="username" type="text">
<br>
<span id="error">{{ errors[0] }}</span>
</p>
</ValidationProvider>
<button @click="sendForm" :disabled="invalid" type="button">Submit</button>
</ValidationObserver>
</template>
<script>
import { ValidationProvider, ValidationObserver, extend } from "vee-validate";
import { required } from "vee-validate/dist/rules";
extend("required", {
...required
});
export default {
data: function() {
return {
username: null
};
},
components: {
ValidationProvider,
ValidationObserver
},
methods: {
sendForm() {
alert(`Thanks ${this.username}`)
}
}
};
</script>
Let's add the method that calls our API. For this example I'll use the Github API to look up usernames. The endpoint URI is https://api.github.com/users/:username
.
Github returns a 404 when the username is not found, in our case that means the field is valid. Whatever your situation is, this method should return true
if valid and false
if not.
export default {
// ...
methods: {
async isUsernameUnique() {
try {
const response = await axios.get(
`https://api.github.com/users/${this.username}`
);
return false;
} catch (err) {
if (err.response.status === 404) {
return true;
}
}
}
}
// ...
}
Now that we have the method in place we can tell VeeValidate to use it in our new rule.
export default {
// ...
mounted() {
extend("unique", {
validate: this.isUsernameUnique,
message: "Username already taken"
});
}
// ...
}
Lastly, we add the rule to the ValidationProvider
.
<ValidationProvider name="username" rules="required|unique" :debounce="500" v-slot="{ errors }">
<p>
<input placeholder="username" v-model="username" type="text">
<br>
<span id="error">{{ errors[0] }}</span>
</p>
</ValidationProvider>
Note that I've added a :debounce
attribute. This ensures we won't be overflowing the API with requests at every keypress but rather every 500ms.
You can find the entire source code for this example on CodeSandbox.
Using handleSubmit
To prevent the user from sending the form before we have heard back from our API we can use handleSubmit
. It takes our own sendForm
method as an argument and using it is very straightforward.
<ValidationObserver v-slot="{ invalid, handleSubmit }">
<!-- the rest of our form -->
<button @click="handleSubmit(sendForm)" :disabled="invalid" type="button">Submit</button>
</ValidationObserver>
Hopefully this simple example will be useful for writing your own server side validation rules.
Top comments (1)
Thanks for the example. There is no example how to do this anywhere with v3 other than this one. My question is how can I do this when registering the components globally? I can't figure out how to call the api using a different function for each form with the
extend("unique", {
When it's registered globally in my main.js