f1uk3r
f1uk3r

Reputation: 317

Validating a non existent field error when trying to use vee-validate

I am trying to make a multistep form. For that I made some Input, Dropdown Components.

FormInput.vue

<template>
  <div class="field">
    <label class="label has-text-left"
    :for="fieldId">
      {{ labelText }} 
    </label>
    <div class="control">
      <input 
      v-model="inputValue"
      v-bind="$attrs" 
      class="input"
      :id="fieldId"
      @input="event => {$emit('input', event.target.value)}"
      :class="{'is-danger': error, 
      'is-success': !error && instance && instance!==''}" >
    </div>
    <p class="help has-text-left danger"
    v-show="error">
      {{ error }}
    </p>
  </div>
</template>

<script>
export default {
  name: 'FormInput',
  props: {
    labelText: String,
    instance: [Number, String],
    fieldId: String,
    error: String
  },
  data () {
    return {
      inputValue: null
    }
  },
  mounted () {
    this.inputValue = this.instance
  },
  $_veeValidate: {
    name () {
      this.fieldId
    },
    value () {
      this.instance
    }
  },
  watch: {
    instance: function(newValue) {
      this.inputValue = newValue
    }
  }
}
</script>

I am using this FormInput components in many forms. Eg. QuerySportsFitnessForm.vue

<div class="modal-form" v-if="step === 5">

  <FormDropdown 
  v-if="form.standard !== 'dietician'"
  labelText="What is the gender of the student??"
  v-model="form.gender"
  :options="genderArray"
  :instance="form.gender"
  ref="gender"></FormDropdown>

  <FormInput
  type="text"
  labelText="Your locality"
  v-model="form.location"
  :instance="form.location"
  fieldId="location"
  v-validate="'required'"
  name="location"
  :error="errors.first('location')"
  placeholder="eg.Heerapura"></FormInput>

  <FormInput
  type="textarea"
  labelText="Your full address"
  v-model="form.address"
  :instance="form.address"
  fieldId="address"
  v-validate="'required|min:10'"
  name="address"
  :error="errors.first('address')"
  placeholder="Enter your postal address"></FormInput>

  <FormInput
  type="textarea"
  labelText="Anything else you would like to share with us?"
  v-model="form.comment"
  :instance="form.comment"
  fieldId="comment"
  placeholder="Comment here"></FormInput>

</div>
<script>
import axios from 'axios'
import FormInput from './FormInput'
import FormDropdown from './FormDropdown'
import FormCheckbox from './FormCheckbox'

export default {
  components: {
    FormDropdown,
    FormInput,
    FormCheckbox
  },
  name: 'QuerySportsFitnessForm',
  props: {
  },
  data () {
    return {
      modalStatus: false,
      step: 1,
      progressValue: 0,
      form: {
        fees: 1000,
        category: 'Sports and Fitness',
        standard: '',
        subjects: [],
        level: '',
        type_coaching: '',
        travel: '',
        number_of_student: '',
        coaching_location: '',
        days: '',
        gender_preference: '',
        location: '',
        address: '',
        name: '',
        age: '',
        contact: '',
        email: '',
        gender: '',
        comment: ''
      },
      typeCoachingArray: [
        { value: "batch", text: "In a Batch"},
        { value: "1-on-1", text: "1-on-1 Sessions"},
        { value: "flexible", text: "Flexible to get best tutor"}
      ],
      travelArray: [
        { value: "0-1", text: "0-1 Km"},
        { value: "0-3", text: "0-3 Km"},
        { value: "0-5", text: "0-5 Km"},
        { value: "more than 5", text: "More than 5 Km"}
      ],
      coachingLocationArray: [
        { value: "at home", text: "At Home"},
        { value: "at tutor", text: "At Tutor's Location"},
        { value: "flexible", text: "Flexible to get best tutor"},
      ],
      genderPreferenceArray: [
        { value: "male", text: "Male" },
        { value: "female", text: "Female" },
        { value: "none", text: "No preference" }
      ],
      daysArray: [
        { value: "1", text: "1 day a week" },
        { value: "2", text: "2 days a week" },
        { value: "3", text: "3 days a week" },
        { value: "4", text: "4 days a week" },
        { value: "5", text: "5 days a week" },
        { value: "6", text: "6 days a week" },
        { value: "7", text: "7 days a week" }
      ],
      levelArray: [
        { value: "beginner", text: "Beginner" },
        { value: "intermediate", text: "Intermediate" },
        { value: "advanced", text: "Advanced" }
      ],
      genderArray: [
        { value: "male", text: "Male" },
        { value: "female", text: "Female" }
      ],
      numberOfStudentArray: [
        { value: "One", text: "One" },
        { value: "Two", text: "Two" },
        { value: "Three", text: "Three" },
        { value: "More than Three", text: "More than Three" },
      ],
      dieticianSubjects: [
        "Weight Loss",
        "Weight Gain",
        "Health Condition",
        "Sports Conditioning Diets"
      ],
      martialArtsSubjects: [
        "Karate",
        "Taekwondo",
        "Wing Chun",
      ],
      trainerSubjects: [
        "General Fitness",
        "Intermediate Bodybuilding",
        "Hardcore Bodybuilding"
      ],
      yogaSubjects: [
        "Power Yoga",
        "Therapeutic Yoga",
        "Yoga for Senior Citizen",
        "Pregnancy Yoga",
        "Yoga for Everyone"
      ]
    }
  },
  mounted () {
    this.form.standard = this.$route.params.standard
    if (['dietician', 'trainer', 'yoga', 'martial-arts'].indexOf(this.form.standard) !== -1) {
      this.step = 1
    }
    else {
      this.step = 2
    }
  },
  methods: {
    modalToggle() {
      this.modalStatus = !this.modalStatus
    },
    /*addToInstance method is corrosponding to checkbox component*/
    addToInstance(e) {
      if (this.form.subjects.indexOf(e) ===-1) {
        this.form.subjects.push(e)
      }
      else {
        this.form.subjects.splice(this.form.subjects.indexOf(e), 1)
      }
    },
    prev() {
      this.progressValue -= 25
      this.step--;
    },
    next() {
      if (this.step === 1) {
        this.$validator.validate('subjects', this.form.subjects)
        if (this.form.subjects.length!==0) {
          this.step++
          this.progressValue += 20
        }
      }
      else if (this.step === 2) {
        this.$refs.level.dropdownToggle()
        this.$refs.genderPreference.dropdownToggle()
        this.$refs.days.dropdownToggle()
        if (['dietician', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) === -1) {
          if (this.form.level !== '' && this.form.days !== '') {
            this.step++
            this.progressValue += 20
          }
        }
        else if (['dietician', 'trainer', 'yoga'].indexOf(this.form.standard) !== -1) {
          if(this.form.gender_preference !== '' && this.form.days !== '') {
            this.step++
            this.progressValue +=20
          }
        }
        else if (this.form.standard === 'zumba') {
          if (this.form.days !== '') {
            this.step++
            this.progressValue += 20
          }
        }
      }
      else if (this.step === 3) {
        if (this.form.standard !== 'dietician') {
          this.$refs.typeCoaching.dropdownToggle()
        }
        if (['chess', 'skating', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) !== -1 && this.form.type_coaching!=='batch') {
          this.$refs.coachingLocation.dropdownToggle()
        }
        if (this.form.coaching_location !== 'at home') {
          this.$refs.travel.dropdownToggle()
        }
        if (this.form.standard !== 'dietician') {
          if (this.form.type_coaching !== '') {
            if (['chess', 'skating', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) !== -1) {
              if (this.form.type_coaching !== 'batch' && this.form.coaching_location !=='') {
                if (this.form.coaching_location !== 'at home' && this.form.travel !== '') {
                  this.step++
                  this.progressValue += 20
                }
                else if (this.form.coaching_location === 'at home' && this.form.travel === '') {
                  this.step++
                  this.progressValue += 20
                }
              }
              else if (this.form.type_coaching === 'batch' && this.form.travel !== '') {
                this.step++
                this.progressValue += 20
              }
            }
            else if (['chess', 'skating', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) === -1) {
              if (this.form.travel !== '') {
                this.step++
                this.progressValue += 20
              }
            }
          }
        }
        else if (this.form.standard === 'dietician') {
          if (this.form.coaching_location !== 'at home' && this.form.travel !== '') {
            this.step++
            this.progressValue += 20
          }
          else if (this.form.coaching_location === 'at home' && this.form.travel === '') {
            this.step++
            this.progressValue += 20
          }
        }
      }
      else if (this.step === 4) {
        this.$validator.validate('name', this.form.name)
        this.$validator.validate('contact', this.form.contact)
        this.$validator.validate('email', this.form.email)
        this.$validator.validate('age', this.form.age).then(() => {
          if (this.errors.items.length === 0) {
            this.step++
            this.progressValue += 20
          }
        })
      }
    },
    //Problem with this block I think
    validateBeforeSubmit() {
      this.$refs.gender.dropdownToggle()
      this.$validator.validate('location', this.form.location)
      this.$validator.validate('address', this.form.address)
      if (this.form.standard === 'dietician') {
        if (this.errors.items.length === 0) {
          this.addQuery()
          this.modalToggle()
        }
      }
      else if (this.form.standard !== 'dietician') {
        if (this.errors.items.length === 0 && this.form.gender !== '') {
          this.addQuery()
          this.modalToggle()
        }
      }
    },
    emptyCoachingLocation() {
      this.form.coaching_location = ''
    },
    emptyTravel() {
      this.form.travel = ''
    },
    addQuery() {
      axios({
        method: 'post',
        url: 'http://127.0.0.1:8000/api/hobby-query/',
        data: {
          fees: this.form.fees,
          category: this.form.category,
          standard: this.form.standard,
          subjects: this.form.subjects,
          level: this.form.level,
          type_coaching: this.form.type_coaching,
          travel: this.form.travel,
          number_of_student: this.form.number_of_student,
          coaching_location: this.form.coaching_location,
          days: parseInt(this.form.days),
          gender_preference: this.form.gender_preference,
          location: this.form.location,
          address: this.form.address,
          name: this.form.name,
          age: parseInt(this.form.age),
          contact: this.form.contact,
          email: this.form.email,
          student_gender: this.form.gender,
          comment: this.form.comment
        }
      }).then(() => { 

        this.form.subjects = [],
        this.form.level = '',
        this.form.type_coaching = '',
        this.form.travel = '',
        this.form.number_of_student = '',
        this.form.coaching_location = '',
        this.form.days = '',
        this.form.gender_preference = '',
        this.form.location = '',
        this.form.address = '',
        this.form.name = '',
        this.form.age = '',
        this.form.contact = '',
        this.form.email = '',
        this.form.gender = '',
        this.form.comment = ''

      })
      .catch((error) => {
        console.log(error);
      });
    }
  }
}
</script>

I only included one div for example. When I am click "next" button method next() works without any problem. When I click submit button it fires up validateBeforeSubmit() method. And I get this error 2 times in console of developer tools.

Uncaught (in promise) Error: [vee-validate] Validating a non-existent field: "". Use "attach()" first.
    at createError (vee-validate.esm.js?00d1:297)
    at Validator._handleFieldNotFound (vee-validate.esm.js?00d1:2282)
    at Validator.validate (vee-validate.esm.js?00d1:1959)
    at ScopedValidator.validate (vee-validate.esm.js?00d1:3276)
    at VueComponent.validateBeforeSubmit (QuerySportsFitnessForm.vue?cb73:448)
    at invoker (vue.esm.js?efeb:2027)
    at HTMLButtonElement.fn._withTask.fn._withTask (vue.esm.js?efeb:1826)
createError @   vee-validate.esm.js?00d1:297
_handleFieldNotFound    @   vee-validate.esm.js?00d1:2282
validate    @   vee-validate.esm.js?00d1:1959
validate    @   vee-validate.esm.js?00d1:3276
validateBeforeSubmit    @   QuerySportsFitnessForm.vue?cb73:448
invoker @   vue.esm.js?efeb:2027
fn._withTask.fn._withTask   @   vue.esm.js?efeb:1826

I tried to give .then promise after validate method call also but it didn't changed anything. like this

    validateBeforeSubmit() {
      this.$refs.gender.dropdownToggle()
      this.$validator.validate('location', this.form.location)
      this.$validator.validate('address', this.form.address).then(() {
        if (this.form.standard === 'dietician') {
          if (this.errors.items.length === 0) {
            this.addQuery()
            this.modalToggle()
          }
        }
        else if (this.form.standard !== 'dietician') {
          if (this.errors.items.length === 0 && this.form.gender !== '') {
            this.addQuery()
            this.modalToggle()
          }
        }
      }) 
    }

What am i doing wrong, I am not able to figure it out myself

Upvotes: 1

Views: 3382

Answers (2)

Sevymol Sebastian
Sevymol Sebastian

Reputation: 103

The problem seems to be that the validation's value change listener on the input was getting fired when cleared the variable, which then tried to validate the input, but the input was no longer in the validator's field list because it was detached when it was hidden.

this.$validator.pause()
this.$nextTick(() => {
    this.$validator.errors.clear()
    this.$validator.fields.items.forEach(field => field.reset())
    this.$validator.fields.items.forEach(field => this.errors.remove(field))
    this.$validator.resume()
})

This code will solve the issue.

Upvotes: 1

Anik Saha
Anik Saha

Reputation: 4494

I think this issue is happening because either location or address is not present in html due to step === 5 condition and you still try to validate that part. You can put below condition to get rid of this error===>

if (this.$validator.fields.find({ name: 'location' })) {
            this.$validator.validate('location');
}

Upvotes: 3

Related Questions