Vee-validate: Custom component validation

Created on 26 Oct 2017  ยท  10Comments  ยท  Source: logaretm/vee-validate

Versions:

  • VueJs: 2.5.0
  • Vee-Validate: 2.0.0-rc.19

Description:

I have forms components all over my application which emit on my Bus the "name" when the value is changed : Events.$emit(this.name, this.savedValue);
Then, on the parent component, I listen for the change (I couldn't use the v-model for some reasons at the time) like this:
Events.$on('myName', (value) => { this.myData.name = value; });

I would ideally like to add the validator on the parent level - ie. on my custom component: - as they may change all over my app.

I can't make the listen work here. What would be the best way?

Any help would be appreciated.

Thanks.

โ” question

Most helpful comment

I can't help much without an example, as there are multiple solutions. First you may need to tell your subcomponent to inherit the validator from their parent which should make it easier to validate them using the Inject API.

When they will receive the validator from their parent, meaning they have access to the same fields and errors

// your component ctor options.
export default {
  inject: ['$validator']
};

Now since your components have access to the parent validator object as their own, they can either register themselves as fields, or register some inputs inside them, depending on how your components look like.

export default {
  created () {
    // attach field
    this.$validator.attach({
      name: this.name, // or whatever the field name is
      rules: 'required',
      getter: () => {
        return this.myData.value; // or whatever the value path is.
      }
    });

    // add bus listener
    Events.$on('myName', (value) => {
      this.myData.name = value;
      this.$validator.validate(this.name).then(result => {
        // do stuff
      });
    });
  }
};

Again I might complicate things here, if you can provide a minimal example in a fiddle I would be happy to help more.

All 10 comments

I can't help much without an example, as there are multiple solutions. First you may need to tell your subcomponent to inherit the validator from their parent which should make it easier to validate them using the Inject API.

When they will receive the validator from their parent, meaning they have access to the same fields and errors

// your component ctor options.
export default {
  inject: ['$validator']
};

Now since your components have access to the parent validator object as their own, they can either register themselves as fields, or register some inputs inside them, depending on how your components look like.

export default {
  created () {
    // attach field
    this.$validator.attach({
      name: this.name, // or whatever the field name is
      rules: 'required',
      getter: () => {
        return this.myData.value; // or whatever the value path is.
      }
    });

    // add bus listener
    Events.$on('myName', (value) => {
      this.myData.name = value;
      this.$validator.validate(this.name).then(result => {
        // do stuff
      });
    });
  }
};

Again I might complicate things here, if you can provide a minimal example in a fiddle I would be happy to help more.

Hi,

thanks for your help here - really appreciated.

And I'd register in the parent component (I mean where I have my app-input) the inject: ['$validator'], right ?

Regarding the second part of your comment, why can I use the v-validate inline in my app-input in the parent ?

Basically, my Vue files looks like (simplified) the following.
The component containing the form:

https://gist.github.com/bdebever/7e71425ac5d80546bb64c2a15ed48422

and the input component:
https://gist.github.com/bdebever/e8f50e4e8990ec8bcafd712bbc97efdc

thanks.

You can disregard the second part. I assumed you didn't want to use the v-validate directive.

You don't have to do any registration now, the directive will take care of these things. What inject does is to let the input component inherit the same validator as their parent, thats it. So if it validates itself the errors will appear in the parent component as well.

Now when the value changes by an external event, you can just call this.$validator.validate('fieldName') and errors will appear in the parent errors as well.

Hi, okay it worked.

I have an issue though - when the instance is mounted, even if there is already a value, the error is displayed "this field is required". So it doesn't take into account a pre-existing value.

Can you help?

thanks;

You can provide the value as the second parameter, since it doesn't detect it properly.

this.$validator.validate('fieldName', inputValue);

In the mounted/created instance?

Whenever the value changes, in your listener for example and if you are validating initially then in the created cycle listener as well.

The problem is that in the created hook, my component isn't fully mounted - so it throws an error, and in the mounted the value isn't passed.

Any other idea?

I am trying to use vee-validate with custom bootstrap input wrapper (https://bootstrap-vue.js.org/docs/components/form-input) and found that it's not fully integrated because it need to listen native events in order to get notified on blur (https://vuejs.org/v2/guide/components.html#Binding-Native-Events-to-Components).

My workaround is to call validation explicitly in submit handler:

<script>
  export default {
    data() {
      return {
        form: {
          email: ''
        },
        submiting: false
      }
    },
    methods: {
      onSubmit(e) {
        e.preventDefault();

        this.$validator.validateAll();
        if (this.errors.any()) {
          console.log('errors!!!');
          return;
        }

        this.submiting = true;
        axios.post('/contact', this.form).then((response) => {
          this.submiting = false;
          if (response.errors) {
            console.log(response.errors); // TODO: append to this.errors
          } else {
            console.log('Success!');      // TODO: redirect to another screen
          }
        });
      }
    }
  };
</script>

<template>
  <div>
    <b-form @submit="onSubmit" class="mx-lg-5" novalidate>
      <b-form-group id="emailGroup" label="Email address" label-for="email" >
        <b-form-input id="email"
                      type="email"
                      name="email"
                      v-model="form.email"
                      placeholder="Your e-mail"
                      required
                      v-validate="'required|email'"
                      :state="fields.email.validated ? !errors.has('email') : null" />
        <div v-show="errors.has('email')" class="invalid-feedback">{{ errors.first('email') }}</div>
      </b-form-group>

      <b-button type="submit" variant="primary" :disabled="submiting || errors.any()">Submit</b-button>
    </b-form>
  </div>
</template>

Any suggestion on how to fix it properly? Sorry, I am new in vue.js so maybe I am missing something.

@rzaharenkov

If the component does not emit the blur event then you cannot validate it on blur unfortunately, if your solution works then stick with it. You might consider building your own components if you want to overcome the 3rd party components limitations.

Closing since the issue is not related to vee-validate but rather to the app logic

Was this page helpful?
0 / 5 - 0 ratings

Related issues

the94air picture the94air  ยท  3Comments

7immer picture 7immer  ยท  3Comments

MeltedFreddo picture MeltedFreddo  ยท  3Comments

yyyuuu777 picture yyyuuu777  ยท  3Comments

triffer picture triffer  ยท  3Comments