vee-validate
vee-validate copied to clipboard
My form validation is showing as invalid when it is most definitely valid using rule required
What happened?
using a nuxt project with nuxt-validate 1.0.1 which i believe uses vee-validate ^3.
I have a custom text field component with vuetify 2 as below
<template>
<ValidationProvider
v-slot="{ errors, valid }"
:immediate="immediate"
:name="$attrs.label"
:rules="rules"
>
<v-text-field
v-model="innerValue"
:error-messages="errors"
:success="valid"
v-bind="$attrs"
v-on="$listeners"
></v-text-field>
</ValidationProvider>
</template>
<script>
import { ValidationProvider } from "vee-validate";
export default {
name: "VTextFieldWithValidation",
components: {
ValidationProvider,
},
props: {
rules: {
type: [Object, String],
default: "",
},
// must be included in props
value: {
type: null,
default: null,
},
immediate: {
type: Boolean,
default: false,
required: false,
},
},
data: () => ({
innerValue: "",
}),
watch: {
// Handles internal model changes.
innerValue(newVal) {
this.$emit("input", newVal);
},
// Handles external model changes.
value(newVal) {
this.innerValue = newVal;
},
},
created() {
if (this.value) {
this.innerValue = this.value;
}
},
};
</script>
which I'm using on a user profile component as below
<template>
<v-container>
<ValidationObserver ref="obs" v-slot="{ invalid, validated }">
<v-form>
<h3 class="text-center">Personal</h3>
<v-row no-gutters justify="center">
<v-col cols="12" md="3" class="px-2">
<VTextFieldWithValidation
v-model="form.firstName"
rules="required"
:disabled="!isEditable"
label="First Name"
></VTextFieldWithValidation>
</v-col>
<v-col cols="12" md="3" class="px-2">
<VTextFieldWithValidation
v-model="form.lastName"
rules="required"
:disabled="!isEditable"
label="Last Name"
></VTextFieldWithValidation>
</v-col>
</v-row>
<v-row no-gutters justify="center">
<v-col cols="12" md="3" class="px-2">
<VTextFieldWithValidation
v-model="form.emailAddress"
rules="required|email"
:disabled="!isEditable"
label="E-mail Address"
></VTextFieldWithValidation>
</v-col>
<v-col cols="12" md="3" class="px-2">
<VTextFieldWithValidation
v-model="form.phoneNumber"
rules="required"
:disabled="!isEditable"
label="Phone Number"
></VTextFieldWithValidation>
</v-col>
</v-row>
<h3 class="text-center">Address</h3>
<v-row no-gutters justify="center">
<v-col cols="12" md="6" class="px-2">
<VTextFieldWithValidation
v-model="form.address.addressLine1"
rules="required"
:disabled="!isEditable"
label="Address Line 1"
></VTextFieldWithValidation>
</v-col>
</v-row>
<v-row no-gutters justify="center">
<v-col cols="12" md="6" class="px-2">
<v-text-field
v-model="form.address.addressLine2"
:disabled="!isEditable"
label="Address Line 2"
></v-text-field>
</v-col>
</v-row>
<v-row no-gutters justify="center">
<v-col cols="12" md="2" class="px-2">
<VTextFieldWithValidation
v-model="form.address.city"
rules="required"
:disabled="!isEditable"
label="City"
></VTextFieldWithValidation>
</v-col>
<v-col cols="12" md="2" class="px-2">
<v-select
v-model="form.address.state"
:disabled="!isEditable"
:items="states"
item-text="name"
item-value="code"
persistent-hint
return-object
label="State"
single-line
:hint="`${form.address.state.name}, ${form.address.state.code}`"
></v-select>
</v-col>
<v-col cols="12" md="2" class="px-2">
<VTextFieldWithValidation
v-model="form.address.zipcode"
rules="required"
:disabled="!isEditable"
label="Zip Code"
></VTextFieldWithValidation>
</v-col>
</v-row>
<v-row v-if="isEditable" no-gutters justify="center">
<v-col cols="12" md="3" class="px-2 py-2">
<v-btn
:disabled="invalid || !validated"
color="success"
block
@click.prevent="saveProfile()"
>Save</v-btn
>
</v-col>
</v-row>
</v-form>
</ValidationObserver>
</v-container>
</template>
<script>
import { ValidationObserver } from "vee-validate";
import VTextFieldWithValidation from "../application/inputs/VTextFieldWithValidation";
export default {
props: {
isEditable: {
type: Boolean,
default: false,
},
userProfile: {
type: Object,
default: () => ({
firstName: "",
lastName: "",
emailAddress: "",
phoneNumber: "",
address: {
addressLine1: "",
addressLine2: "",
city: "",
state: "MI",
zipcode: "",
},
}),
},
},
components: {
ValidationObserver,
VTextFieldWithValidation,
},
data() {
return {
isMounted: false,
form: {
firstName: "",
lastName: "",
emailAddress: "",
phoneNumber: "",
address: {
addressLine1: "",
addressLine2: "",
city: "",
state: {
name: "Michigan",
code: "MI",
},
zipcode: "",
},
},
states: [
{
name: "Alabama",
code: "AL",
},
{
name: "Alaska",
code: "AK",
},
{
name: "Michigan",
code: "MI",
}
],
};
},
watch: {
userProfile: {
immediate: true,
deep: true,
handler(newVal) {
console.log("WATCH user profile");
console.log(newVal);
if (newVal && this.isMounted) {
this.form.firstName = newVal.firstName;
this.form.lastName = newVal.lastName;
this.form.emailAddress = newVal.emailAddress;
this.form.phoneNumber = newVal.phoneNumber;
if (newVal.address) {
this.form.address.addressLine1 = newVal.address.addressLine1;
this.form.address.addressLine2 = newVal.address.addressLine2;
this.form.address.city = newVal.address.city;
this.form.address.zipcode = newVal.address.zipcode;
console.log(this.$refs.obs);
// console.log(this.$refs.obs.validated);
// console.log(this.$refs.obs.invalid);
}
this.form.address.state =
newVal && newVal.address && newVal.address.state
? this.states.find((s) => s.code === newVal.address.state)
: {
name: "Michigan",
code: "MI",
};
}
},
},
},
mounted() {
this.isMounted = true;
if (this.isEditable) {
this.$refs.obs.reset();
this.$refs.obs.validate();
}
},
methods: {
saveProfile() {
const postBody = {
profile: {
firstName: this.form.firstName,
lastName: this.form.lastName,
emailAddress: this.form.emailAddress,
phoneNumber: this.form.phoneNumber,
address: {
addressLine1: this.form.address.addressLine1,
addressLine2: this.form.address.addressLine2,
city: this.form.address.city,
state: this.form.address.state.code,
zipcode: this.form.address.zipcode,
},
},
};
this.$emit("save-profile", postBody);
},
},
};
</script>
I have a route: /User/profile/edit that creates a component like this:
<template>
<v-container fluid>
<v-layout column>
<v-row>
<v-col>
<v-row>
<v-col>
<h1 class="text-center">User Profile</h1>
</v-col>
</v-row>
<v-row>
<v-col>
<Profile
:is-editable="true"
:userProfile="userProfile"
@save-profile="saveProfile($event)"
/>
</v-col>
</v-row>
</v-col>
</v-row>
</v-layout>
</v-container>
</template>
when this component is mounted and a console.log(this.$refs.obs) I can see that validation has occurred but the fields are recorded as not valid.
when i look at the refs of obs i see the individual validation providers all that have values and should the rules as required, validated as true but valid to be false. what am i doing wrong here?
I attempted to put these probs in a watch thinking this was an order of operations problem but this doesn't seem to be accurate. I also attempted to move the fields to data instead of just having my fields reference the props direction but that doesn't seem to work either.
Reproduction steps
Recreate the above components. watch the magic not unfold as expected
Version
Vue.js 2.x and vee-validate 3.x
What browsers are you seeing the problem on?
- [X] Firefox
- [X] Chrome
- [ ] Safari
- [X] Microsoft Edge
Relevant log output
No response
Demo link
NA
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
Same issue as https://github.com/logaretm/vee-validate/issues/1084 and https://github.com/logaretm/vee-validate/issues/87
errors updating after the watch was already triggered.