vee-validate icon indicating copy to clipboard operation
vee-validate copied to clipboard

My form validation is showing as invalid when it is most definitely valid using rule required

Open funkel1989 opened this issue 2 years ago • 1 comments

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

funkel1989 avatar Nov 25 '22 19:11 funkel1989

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.

seyfer avatar Mar 28 '23 16:03 seyfer