Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nuxt.js and Vee-Validate Cannot read property 'valid' of undefined #1148

Closed
devanflaherty opened this issue Feb 3, 2018 · 3 comments
Closed
Labels
❔ question More of an inquiry than an issue

Comments

@devanflaherty
Copy link

Versions:

  • Nuxt.js 1.3.0
  • Vee-Validate: 2.0.3

Description:

I've been using Vee-Validate for a year and have used it on multiple nuxt projects.
But I've just ran into something strange today.

I have a basic page that has an area that calls for dynamic components.
Such as this :

<component :is="template + 'Template'" />

This component-block renders a specific template in accordance to what has been selected from the back-end. The nuxt page component makes an http request via async asyncData, and works wonderfully and will render the correct component.

One of the components that will render is a contact component in which I have my form utilizing vee-validate.

Here is the code for that component:

<template>
  <section class="contactPage section" >
    
    <div class="container">
        <form @submit.prevent="onSubmit" v-if="contactEmail">
            <!-- First Name -->
            <div class="field">
              <div class="control has-icons-right">
                <input class="input" type="text" v-model.lazy="contact.firstName" name="firstName" 
                  placeholder="First Name" 
                  v-validate="'required'" 
                  :class="{'is-danger': errors.has('firstName') }">

                <transition name="pop">
                  <span class="icon is-small is-right" v-if=" fields.firstName.valid">
                    <i class="fa fa-check"></i>
                  </span>
                </transition>
              </div>
            </div>

            <!-- Last Name -->
            <div class="field">
              <!-- <label class="label">Last Name</label> -->
              <div class="control has-icons-right">
                <input class="input" type="text" v-model.lazy="contact.lastName" name="lastName" 
                  placeholder="Last Name" 
                  v-validate="'required'" 
                  :class="{'is-danger': errors.has('lastName') }">
                
                <!-- <transition name="pop">
                  <span class="icon is-small is-right" v-if="fields.lastName.valid">
                    <i class="fa fa-check"></i>
                  </span>
                </transition> -->
              </div>
            </div>

            <!-- EMAIL -->
            <div class="field">
              <!-- <label class="label">Email</label> -->
              <div class="control has-icons-right">
                <input class="input" type="email" v-model.lazy="contact.email" name="email" placeholder="Email"
                  v-validate="'required|email'"
                  :class="{'is-danger': errors.has('email') }">
                
                <!-- <transition name="pop">
                  <span class="icon is-small is-right is-check" v-if="fields.email.valid">
                    <i class="fa fa-check"></i>
                  </span>
                </transition> -->
              </div>
            </div>

            <!-- MESSAGE -->
            <div class="field">
              <!-- <label class="label">Comments</label> -->
              <div class="control">
                <textarea class="textarea" v-model.lazy="contact.message" name="message" placeholder="Comments"
                  v-validate="'required'"
                  :class="{'is-danger': errors.has('message') }">
                </textarea>
              </div>
            </div>

            <input type="text" name="_gotcha" style="display:none" />

            <!-- SUBMIT BUTTON -->
            <div class="field is-grouped">
              <div class="control">
                <button @click="onSubmit" class="button is-primary" :class="{'is-loading': sending}">
                  <transition name="fade-in">
                    <span class="icon is-small" v-if="sent">
                      <i class="fa fa-check white"></i>
                    </span>
                  </transition>
                  <span v-if="!sent">Submit</span>
                  <span v-else>Sent</span>
                </button>
              </div>
            </div>
            <!-- Feedback -->
            <div class="feedback">
              <p class="notice" v-if="sent">Thank you for reaching out, someone will be with you in the next 4 hours.</p>
              
              <transition name="fade-in">
                <ul class="form-errors" v-if="errors.any()">
                  <li>
                    <span>Please make sure the form is completely filled out.</span>
                  </li>
                  <li v-if="this.errors.has('email')"><span>{{this.errors.first('email')}}</span></li>
                </ul>
              </transition>
            </div>
          </form>

    </div><!-- close container -->

  </section>
</template>

<script>
import axios from 'axios'
import {mapGetters} from 'vuex'

export default {
  name: 'Contact',
  props: ['page'],
  data () {
    return {
      sending: false,
      sent: false,
      contact: {
        firstName: null,
        lastName: null,
        email: null,
        message: null
      }
    }
  },
  computed: {
    fullName () {
      return this.contact.firstName + ' ' + this.contact.lastName
    },
    ...mapGetters('admin', ['contactEmail'])
  },
  methods: {
    onSubmit () {
      this.$validator.validateAll().then((result) => {
        if (result) {
          // eslint-disable-next-line
          this.post()
        }
      })
    },
    post () {
      this.sending = true
      axios.post(this.contactEmail, {
        'name': this.fullName,
        'email': this.contact.email,
        'message': this.contact.message,
        '_subject': 'Contact from' + this.fullName
      }).then(res => {
        this.sending = false
        this.sent = true
        this.clean()
      }).catch(err => {
        console.log(err)
      })
    },
    async clean () {
      const clear = async () => {
        this.contact.firstName = null
        this.contact.lastName = null
        this.contact.email = null
        this.contact.message = null
      }
      await clear().then(() => {
        this.$validator.reset()
      })
    }
  }
}
</script>

Essentially the error I'm getting when viewing the page is :
Cannot read property 'valid' of undefined

So in the code above I isolated this code block:

<transition name="pop">
    <span class="icon is-small is-right" v-if="fields.firstName.valid">
        <i class="fa fa-check"></i>
    </span>
</transition>

And found that the issue is that when the page renders it doesn't recognize that 'fields.firstName' exists. Interesting enough I began to just output what {{fields}} would return and {{fields.firstName}}' and it showed me results, but if I add {{fields.firstName.valid}}` throws an error again.

So the work around is to make check as so v-if="fields.firstName && fields.firstName.valid".

Anyways I find this all to be odd cause I've never experienced this before.


A few more details about my project.

// plugins/vee-validate.js
import Vue from 'vue'
import VeeValidate from 'vee-validate'

Vue.use(VeeValidate, {
  inject: true
})
// nuxt.config.js
...
{ src: `~plugins/vee-validate`, ssr: true},
...

Note: I've set ssr: false and then it tells me that it can't find 'errors.has('firstName')`.

Just hoping to get an understanding of what is going on.

@devanflaherty devanflaherty changed the title Nuxt.js Nuxt.js and Vee-Validate Cannot read property 'valid' of undefined Feb 3, 2018
@logaretm
Copy link
Owner

That is a side effect for the fix produced in: vuejs/devtools#499

Adding extra check is the correct way to do it since the fields are added at a later time than the creation time. You can use mapFields if you find it tedious to add those checks which they are, but no way around them unfortunately, or until Proxies are well supported in browsers :)

this issue is mentioned in the flags example (although poorly):

http://vee-validate.logaretm.com/examples#flags-example

turning ssr to false won't work because your template uses vee-validate in the template. vee-validate should work fine with SSR.

@logaretm logaretm added the ❔ question More of an inquiry than an issue label Feb 15, 2018
@juniorgarcia
Copy link

juniorgarcia commented Jul 24, 2018

I'm using the following versions:

  • vee-validate: 2.0.9
  • nuxt: 1.4.1

That's how I solved here:

// nuxt.config.js
plugins: [
  {
    src: '~/plugins/vee-validate.js', ssr: true // ssr was set to false before.
  }
]
// ~/plugins/vee-validate.js
import Vue from 'vue';
import VeeValidate from 'vee-validate';
import attributesPtBr from 'vee-validate/dist/locale/pt_BR';

Vue.use(VeeValidate);

Setting inject: false when telling Vue to use VeeValidate didn't work, I got the same Cannot read property 'has' of undefined.

Honestly, I don't understand why this fixed the problem, it was just a lucky guess. Can somebody explain to me?

Edit:
After some research, I found this this comment and it makes use of the same solution.

@logaretm
Copy link
Owner

@juniorgarcia read the docs take on this:

https://baianat.github.io/vee-validate/concepts/injections.html#disabling-automatic-injection

The link may have been changed since the issue you referenced. You still cannot use errors or any injected prop unless your component injects a validator instance which is what you may be missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
❔ question More of an inquiry than an issue
Projects
None yet
Development

No branches or pull requests

3 participants