Vue.js BootstrapVue : Veevalidate cannot validate datepicker - vue.js

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 ?

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.

How can I write emit even to tell parent is icon is clicked in Vuejs

I am write input element with icon to show/hide password, how can I write event emit in v-icon to tell parent component that icon is click
Here is my child component BaseInputPassword
<div class="label">
{{ labelName }} <span v-if="required" class="required">※</span>
</div>
<div class="input_password">
<input
:type="type ? 'password' : 'text'"
:placeholder="placeholder"
/>
<v-icon #click="type =! type">{{ type ? "mdi-eye-off" : "mdi-eye" }}</v-icon>
</div>
</div>
</template>
<script>
export default {
props: ["labelName", "placeholder", "required", "type" ],
data() {
return {
};
},
methods: {
updateValue($event) {
this.$emit("input", $event);
},
}
}
</script>
Here is my parent component, im using three child component
<div class="input_area">
<BlockInputPassword labelName="現在のパスワード" required="true" :type="show1" v-model="password" ></BlockInputPassword>
<BlockInputPassword labelName="現在のパスワード" required="true" :type="show2" v-model="password" ></BlockInputPassword>
<BlockInputPassword labelName="新しいパスワードの確認" required="true" :type="show3" v-model="password"></BlockInputPassword>
</div>
</div>
<div class="footer">
<router-link to=""
><div class="btn_login btn_simple">
キャンセル
</div></router-link>
<router-link to=""
><div class="btn_login btn_simple status_color_4">
変更
</div></router-link>
</div>
</div>
</div>
</template>
<script>
import BlockInputPassword from "../components/common/BlockInputPassword"
export default {
components: {
BlockInputPassword,
},
name: "PasswordChange",
data() {
return {
password: '',
show1: false,
show2: false,
show3: false,
};
},
have you child component v-icon emit a custom event
<v-icon #click="$emit('icon-clicked')">
then from the parent component
<div class="input_area">
<BlockInputPassword #icon-click="doSomething" ...></BlockInputPassword>
...
</div>

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

Vuelidate simple required validation

I try to do a simple validation with Vuelidate, for required, but I get an error, and I am not sure why!?
Thanks!
<form ref="form" #submit.stop.prevent>
<b-form-group>
<b-form-input
v-model="name"
:state="name"
v-model.trim="$v.name.$model"
:class="{
'is-invalid':$v.name.$error, 'is-valid':!$v.name.$invalid}"
></b-form-input>
</b-form-group>
<div class="valid-feedback">Ok!</div>
<div class="invalid-feedback">
<span v-if="!$v.name.required">Not ok!</span>
</div>
</form>
import required from "vuelidate/lib/validators";
validations: {
account_name: {
required
}
}
Error in render: "TypeError: Cannot convert undefined or null to object"
I found the problem.
import { required } from "vuelidate/lib/validators";
Required must have { } .

Vue + Vee Validate 3 Trigger Manual Validation

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>