Vee Validate 3.0 custom classes not applied - vue.js

From the docs, I think I need to use configure to add custom classes to my validated fields, but I can't get it to work.
This is what I have so far...
import { extend, configure, localize } from 'vee-validate'
import { required, min, max } from 'vee-validate/dist/rules'
import en from 'vee-validate/dist/locale/en.json'
// Install rules
extend('required', required)
extend('min', min)
extend('max', max)
// Install classes
configure({
classes: {
valid: 'is-valid',
invalid: 'is-invalid'
}
})
// Install messages
localize({
en
})
And in my view....
<ValidationObserver ref="observer" v-slot="{ invalid }" tag="form" #submit.prevent="checkRef()">
<div class="form-group">
<label for="reference">Reference</label>
<ValidationProvider rules="required|max:20" name="reference" v-slot="{ errors }">
<input maxlength="20" name="reference" v-model="ref" id="reference" class="form-control"/>
<span class="warning">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<button #click="checkRef" class="btn btn-primary app-button">Check Reference</button>
</ValidationObserver>
When I click the button, I see the error message but I don't get the 'in-invalid' class applied to my field.
What am I doing wrong?

VeeValidate does not apply the classes automatically anymore, since v3 you now must bind it yourself. Like errors you can extract classes from the slot props and apply it to your input:
<ValidationProvider rules="required|max:20" name="reference" v-slot="{ errors, classes }">
<input maxlength="20" name="reference" v-model="ref" id="reference" class="form-control" :class="classes" />
<span class="warning">{{ errors[0] }}</span>
</ValidationProvider>

Related

ValidationProvider need to validate after submit Vue 2

I am using ValidationObserver and ValidationProvider for validating the email field.
<ValidationProvider
name="email"
rules="required|max:50|email"
v-slot="{ errors }"
>
<md-field
class="border-round-10 border_box"
:class="{ 'md-invalid': errors.length > 0 }"
>
<label>Email </label>
<md-input
v-model="email"
#blur="greatToSeeYou()"
></md-input>
<span class="md-error">{{ errors[0] }}</span>
</md-field>
</ValidationProvider>
I want to validate the email only after the submit button. I tried so many options but didn't work.
There is 4 modes to configure ValidationProvider: aggressive, lazy, eager and passive.
From your requirement, you would like to validate the form on submission only so you can choose passive mode
<ValidationProvider
name="email"
rules="required|max:50|email"
v-slot="{ errors }"
mode="passive"
>
...
</ValidationProvider>
Here is the codesandbox example I made for your reference:
https://codesandbox.io/s/kind-breeze-rbf27?file=/src/components/HelloWorld.vue:146-293

Vee Validate 3 catch errors on form submit

How do i catch if form submit fails in vee-validate? I have the following component
<template>
<ValidationObserver tag='div' class='bg-white pt-6 pb-6 mb-4' v-slot='{ handleSubmit }'>
<form
#submit.prevent='handleSubmit(onSubmit)'
class='mx-auto rounded-lg overflow-hidden pt-6 pb-6 space-y-10'
:class="{'is-loading' : isLoading}"
>
<div class='flex flex-wrap -mx-3 mb-6'>
<div class='w-full md:w-3/12 px-3 mb-6 md:mb-5'>
<ValidationProvider v-slot='{ classes, errors }' rules='required' name='Anrede'>
<CustomSelect :options='gender'
placeholder='Anrede *'
v-model='form.gender'
:class="classes"
/>
<span class='text-mb-error font-medium text-sm italic'>{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
<div class='block'>
<button
class='bg-secondary hover:bg-secondary-200 text-white py-3 px-4 w-full mt-5 focus:outline-none'
type='submit'
>
Registrieren
</button>
</div>
</form>
</ValidationObserver>
</template>
as method I have the following function
onSubmit () {
alert()
console.log(this.$refs.observer)
this.$emit('onSendRegistration', this.form)
}
which works fine if form is valid but if it fails never gets executed. Where can I catch if form validation fails?
As per the documentation:
[handleSubmit] calls validation like validate and mutates provider's state, accepts a callback to be run only if the validation is successful.
So to run a callback in the case of an error in validation you would have to trigger the validation programmatically using a ref in the ValidationObserver.
Your opening ValidationObserver tag would now look like this:
...
<ValidationObserver tag='div' class='bg-white pt-6 pb-6 mb-4' ref='form'>
...
Your opening form tag should be now like this:
...
<form
#submit.prevent='onSubmit'
class='mx-auto rounded-lg overflow-hidden pt-6 pb-6 space-y-10'
:class="{'is-loading' : isLoading}"
>
...
And your onSubmit method should be something like this:
onSubmit () {
this.$refs.form.validate().then(success => {
if (!success) {
// run your error code here
}
alert('Form has been submitted!');
});
}
Besides this programmatic approach there's no equivalent to handleSubmit for running a callback function after an invalid form submission.
Check the docs for more info about the programmatic access with $refs.

Vee-validate - [Vue warn]: Failed to resolve directive: validate

I want to validate text fields, trying below code :
<input v-validate="result.val=='Required' ? 'required' : ''" v-model="required" :name="f_name" type="text"/>
but getting this error:
app.js:48089 [Vue warn]: Failed to resolve directive: validate
Trying this:
<ValidationProvider name="phone" :rules="required" v-slot="{ errors }">
<input class="form-control" :name="phone" type="text" v-model="form.phone"/>
</ValidationProvider>
According to the migration guide this directive is removed in v3.x :
Fields that had the v-validate directive needs to be wrapped by ValidationProvider component now, and they need to use v-model to properly tag themselves for vee-validate.
So this:
<input type="text" name="field" v-validate="'required'">
<span>{{ errors.first('field') }}</span>
Will be re-written as this:
<ValidationProvider name="field" rules="required" v-slot="{ errors }">
<input type="text" v-model="value">
<span>{{ errors[0] }}</span>
</ValidationProvider>
Your code should be like :
<ValidationProvider name="f_name" :rules="result.val=='Required' ? 'required' : ''" v-slot="{ errors }">
<input v-model="required" :name="f_name" type="text"/>
</ValidationProvider>
You should add this to main.js :
import { ValidationProvider } from 'vee-validate';
Vue.component('ValidationProvider', ValidationProvider);
If you are not using a bundler and using vee-validate in the browser or from a CDN:
<script>
// ...
Vue.component('validation-provider', VeeValidate.ValidationProvider);
// ...
</script>

Test a form which used veevalidate on that form

i using vee-validate version 3.0.11 for validate my form like below
<validation-observer v-slot="{ invalid }" slim>
<validation-provider rules="required" v-slot="{ errors, dirty, invalid}" slim>
<div class="form-group">
<label class="sr-only" for="txtUsername"></label>
<input
autocomplete="off"
id="txtUsername"
name="username"
type="text"
class="form-control txtUsername"
placeholder="Email or Username"
v-model="username"
v-bind:class="{ 'is-invalid': invalid && dirty,'is-valid': !invalid }" />
</div>
</validation-provider>
<validation-provider rules="required" v-slot="{ errors, dirty, invalid}" slim>
<div class="form-group">
<label class="sr-only" for="txtPassword"></label>
<input
id="txtPassword"
name="password"
type="password"
class="form-control"
placeholder="Password"
v-model="password"
v-bind:class="{ 'is-invalid': invalid && dirty,'is-valid': !invalid }" />
</div>
</validation-provider>
<div >
<button
type="button"
name="login"
class="btn btn-primary"
v-on:click="doLogin()"
:disabled="invalid">
Login
</button>
</div>
</validation-observer>
and i wrote some test with chai and mocha
in my test i need to find the button
but when i using find method for find button all html tag between validation-observer tag is not loaded in my wrapper.
my test code is:
// i change it to shallowMount to mount but problem is exist,
// mount does not render any thing between validation-observer tag
const wrapper = mount(LoginView, { sync: false });
describe('Login.vue', () => {
it('some text, () => {
console.log(wrapper.html());
// my log include all of tag except tags between the validation-observer tag
});
});
can some one tell me how i can find my button by using warraper.find(), please?
You are trying to shallow mount a component that needs to be mounted in order to render its children. If you want to ignore ValidationProvider all together you can provide a fake one as shown below.
ContactForm.vue
<template>
<div>
<ValidationProvider rules="required" name="input" v-slot="{ errors }">
<p :style="{color: 'red'}">To be, or not to be</p>
<input type="text" v-model="value">
<span id="error">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</template>
<script>
import { ValidationProvider } from "vee-validate";
export default {
name: "ContactForm",
components: {
ValidationProvider
},
data: () => ({
value: ""
})
};
</script>
ContactForm.test.js
import { shallowMount, createLocalVue } from "#vue/test-utils";
import ContactForm from "./ContactForm";
import FakeValidationProvider from "./FakeValidationProvider";
test("Test shallow mount renders what's inside validation provider", async () => {
const localVue = createLocalVue();
var wrapper = shallowMount(ContactForm, {
stubs: {
ValidationProvider: FakeValidationProvider
},
localVue
});
expect(wrapper.text()).toContain("To be, or not to be");
});
FakeValidationProvider.vue
<template>
<div v-bind="{ ...$props, ...$attrs }">
<slot :errors="errors"></slot>
</div>
</template>
<script>
export default {
name: "FakeValidationProvider",
data() {
return {
errors: []
};
}
};
</script>
Feel free to extend the slot with any other parameters you need besides errors. If you want to make those parameters dynamic as well, check out this article on rendering slots

vee validate "required" validation is working only when on change of the field

Below is my code. Three fields are there in my form. Firstname, Middlename, Lastname. All these fields are set as required one. Only difference is that "Firstname & Lastname" contains input tag, but "Middlename" contains b-form-input tag. Actually my problem is, When I focus out(blur) these("Firstname & Lastname") fields it throws an required error but when I do the same for ("Middlename") field it doesn't Image1. I have to enter the value in that ("Middlename") field Image2 ,then I removed the value it's throwing error
Image3 i.e. required validation is only working "on change" of the "("Middlename") field. What is the reason for this?
<template>
<b-card>
<h4 slot="header" class="card-title">Employee</h4>
<b-row>
<b-col sm="3">
<b-form-group>
<label for="name">First Name </label>
<input type="text" id="" class="form-control" placeholder="Enter your name" v-validate="'required'" name="Firstname">
<span v-show="errors.has('Firstname')" class="is-danger">{{ errors.first('Firstname') }}</span>
</b-form-group>
</b-col>
<b-col sm="3">
<b-form-group>
<label for="name">Middle Name </label>
<b-form-input type="text" id="" class="form-control" placeholder="Enter your name" v-validate="'required'" name="Middlename"> </b-form-input>
<span v-show="errors.has('Middlename')" class="help is-danger">{{ errors.first('Middlename') }}</span>
</b-form-group>
</b-col>
<b-col sm="3">
<b-form-group>
<label for="name">Last Name </label>
<input type="text" id="" class="form-control" placeholder="Enter your middle name" v-validate="'required|Name'" name="Lastname">
<span v-show="errors.has('Lastname')" class="help is-danger">{{ errors.first('Lastname') }}</span>
</b-form-group>
</b-col>
</b-row>
<input type="submit" value="Submit" #click="validateForm">
</b-card>
</template>
<script>
import Vue from 'vue'
import VeeValidate from 'vee-validate';
Vue.use(VeeValidate);
export default {
name: 'addEmpl',
created: function() {
this.$validator.extend('Name', {
getMessage: field => '* Enter valid ' + field + '',
validate: value => /^[a-zA-Z]*$/.test(value)
});
}
}
</script>
<style lang="scss" scoped>
.is-danger{
color: RED;
}
</style>
First of all, I am also new to Vue.js and Vee-validate so I stand corrected.
The difference between the 2 fields and the middlename field is that it is a component instead of native input field.
In essence, you need to make the component behaves like an input, i.e. emits the necessary events so Vee-validate can pick up on them ('input', 'change', 'blur' etc).
For e.g if your component is a div wrapping on input it would be something like this:
<template>
<div class="myClassForDiv">
<input
type="text"
class="myClass"
#blur="$emit('blur')"
#input="$emit('input', $event.target.value)"
>
</div>
</template>
<script>
export default {
name: "b-form-input"
}
</script>
The part relevant to the question is #blur="$emit('blur')". Without this line, VeeValidate has no way of getting notified of a blur event occurring hence does not validate (I don't know what other events VeeValidate listens to by default, but 'blur' works for me and 'focusout' does not. You can always use the data-vv-validate-on directive for special events specific to your components).
You may refer to the this article by the maintainer of VeeValidate library. Notice how he creates a component that plays nicely with VeeValidate by making the component emits all the right events.
https://medium.com/#logaretm/authoring-validatable-custom-vue-input-components-1583fcc68314