Vue + Vee Validate 3 Trigger Manual Validation - vue.js

I've almost got my form validation working - I'm using Vee Validate 3 with Vue.js. Most of the examples of Vee Validate are for version 2, so I'm struggling a bit.
The issue I have is triggering the validation when submitting the form.
If I click the text field to focus on it first, then click submit, the validation fires and I see the error message.
If however, I don't click the text field first and just click the submit button, I don't see the error message.
How can I make this work without having to focus on the text field before I click submit?
Update
Weirdly, the console shows the error TypeError: this.validate is not a function in both cases - whether the validation works or not.
<ValidationProvider rules="required" v-slot="{ validate, errors }">
<div>
<input type="text" rules="required">
<p id="error">{{ errors[0] }}</p>
</div>
</ValidationProvider>
<script>
export default {
methods: {
async validateField() {
const valid = await this.validate()
}
}
};
</script>

Adam pointed me in the right direction with the ValidationObserver.
This code works for me...
<ValidationObserver ref="observer" v-slot="{ invalid }" tag="form" #submit.prevent="submit()">
<ValidationProvider rules="required" v-slot="{ errors }">
<input type="text">
<p id="error">{{ errors[0] }}</p>
</ValidationProvider>
<button #click="submit()">Submit>/button>
</ValidationObserver>
<script>
import { VslidationProvider, ValidationObserver } from 'vee-validate'
import { required } from 'vee-validate/dist/rules'
export default {
methods: {
async submit () {
const valid = await this.$refs.observer.validate()
}
}
};
</script>

New way of doing it
<ValidationObserver v-slot="{ handleSubmit }">
<ValidationProvider rules="required" v-slot="{ errors }">
<input type="text">
<p id="error">{{ errors[0] }}</p>
</ValidationProvider>
<button #click="handleSubmit(onSubmit)">Submit>/button>
</ValidationObserver>
<script>
import { VslidationProvider, ValidationObserver } from 'vee-validate'
export default {
methods: {
onSubmit() {
// ...
}
}
};
</script>

Related

vee-validate model-less validation on submit not working

this seems to be simple one but I can not get it work...
I want to validate if file is set only when I click the validate button. but the validation result in check method always return false.
<template>
<ValidationObserver>
<form #submit.prevent>
<ValidationProvider
ref="aProvider"
name="file"
rules="required"
v-slot="{ errors }"
>
<input type="file" />
<p>{{ errors[0] }}</p>
</ValidationProvider>
<button #click="check">validate!</button>
</form>
</ValidationObserver>
</template>
<script>
export default {
methods: {
async check() {
const { valid } = await this.$refs.aProvider.validate();
if (valid) {
alert("Form has been submitted!");
}
},
},
};
</script>
codesandbox https://codesandbox.io/s/codesandbox-forked-6o7iyt?file=/src/Demo.vue
The validate() method is for Observers, not for providers as specified in docs: https://vee-validate.logaretm.com/v2/guide/components/validation-observer.html#methods
Just set a ref on your Observer and run the method.
<template>
<ValidationObserver ref="form">
<form #submit.prevent>
<ValidationProvider
name="file"
rules="required"
v-slot="{ errors }"
>
<input type="file" />
<p>{{ errors[0] }}</p>
</ValidationProvider>
<button #click="check">validate!</button>
</form>
</ValidationObserver>
</template>
<script>
export default {
methods: {
async check() {
const { valid } = await this.$refs.form.validate();
if (valid) {
alert("Form has been submitted!");
}
},
},
};
</script>
Create a data property and bind it to the input via v-model. Move your ref to the ValidationObserver. Then it will validate properly.

Vue.js BootstrapVue : Veevalidate cannot validate datepicker

In my Laravel+Vue.js SPA ( Single Page Application) I am using the datepicker package from here, BootstrapVue from here and Veevalidate from here .
I think I need to show only the code inside my component file as only the datepicker component causes the problem and not other ones. My code follows in EditProfile.vue:
<ValidationObserver ref="form" v-slot="{ passes }">
<div id="registration_form">
<b-form #submit.prevent="passes(onSubmit)" #reset="resetForm">
<ValidationProvider vid="name" rules="required|min:2" name="name" v-slot="{ valid, errors }">
<b-form-group
label="User Name:"
label-for="exampleInput1"
>
<b-form-input
type="text"
v-model="name"
:state="errors[0] ? false : (valid ? true : null)"
placeholder="Enter your name"
></b-form-input>
<b-form-invalid-feedback id="inputLiveFeedback">{{ errors[0] }}</b-form-invalid-feedback>
</b-form-group>
</ValidationProvider>
<ValidationProvider vid="dob" rules="required" name="dob" v-slot="{ valid, errors }">
<b-form-group
label="Date of Birth:"
label-for="exampleInput1"
>
<datepicker
type="text"
v-model="dob"
required
format="d-M-yyyy"
:state="errors[0] ? false : (valid ? true : null)"
>
</datepicker>
<b-form-invalid-feedback id="inputLiveFeedback">{{ errors[0] }}</b-form-invalid-feedback>
</b-form-group>
</ValidationProvider>
<b-button type="submit" variant="primary">Submit</b-button>
<b-button type="reset" variant="danger">Reset</b-button>
</b-form>
</div><!-- end of id registration_form-->
</ValidationObserver>
JS part inside EditProfile.vue is:
import Datepicker from 'vuejs-datepicker';
import { ValidationObserver, ValidationProvider } from "vee-validate";
export default {
name: "EditProfile",
components: {
ValidationObserver,
ValidationProvider,
Datepicker
},
data: () => ({
name: "",
dob:""
}),
methods: {
onSubmit() {
console.log("Form submitted yay!");
},
resetForm() {
this.name = "";
this.dob = "";
requestAnimationFrame(() => {
this.$refs.form.reset();
});
}
}
};
When the submit button is pressed then validation works for name field but no error message shows up when dob field on datepicker is empty .
My Vue.js version is 2.6.10 and I used the latest versions of BootstrapVue and Veevalidate (3) as well.
How can I make the validation work for the date picker as well ?

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

eslint vee validate no-unused-vars

Using Vee Validate with Vue and am getting the following errors when compiling:
error: 'required' is defined but never used (no-unused-vars)
error: 'max' is defined but never used (no-unused-vars)
My code snippet is below
<ValidationObserver ref="observer" v-slot="{ invalid }" tag="form" #submit.prevent="submit()">
<div class="form-group">
<label for="password">Password</label>
<ValidationProvider rules="required|max:50" v-slot="{ errors }">
<input type="password" v-model="password" name="password" class="form-control"/>
<span>{{ errors[0] }}</span>
</ValidationProvider>
</div>
</ValidationObserver>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate'
import { required, max } from 'vee-validate/dist/rules'
export default {
components: {
ValidationProvider,
ValidationObserver
}
}
</script>
I can suppress the errors by adding this code
Vue.use(required)
Vue.use(max)
But is that the correct way or is there a setting I can use in eslint to prevent these errors?
Turns out it was quite easy, but I'm still unsure if this is the best way - if someone has a better answer, please post!
/* eslint-disable no-unused-vars */
import { required, max } from 'vee-validate/dist/rules'
/* eslint-enable no-unused-vars */
UPDATE
I think the official way is to extend each rule you're using:
extend('required', required)
extend('max', max)

Update component data from the template

Not too sure what is wrong here, it seems fine to me! I'm simply trying to update the data property display to true when I click the input within my component.
I have passed the data to the slot scope, so can't see that being the issue. It just simply won't update, using a function to toggle it works, however not what I really want to do, seems pointless.
<time-select>
<div slot-scope="{ time }" class="is-inline-block">
<label for="businessHoursTimeFrom"><i class="fas fa-clock"></i> From</label>
<input type="text" name="businessHoursTimeFrom[]" v-model="time" v-on:click="display = true">
</div>
</time-select>
The code behind:
<template>
<div>
<p>{{ display }}</p>
<slot :time="time" :display="display"></slot>
<div class="picker" v-if="display">
<p>Test</p>
</div>
</div>
</template>
<script>
export default {
props: [],
data: function () {
return {
time: '',
display: false
}
},
mounted() {
},
methods: {
}
}
</script>