How to modify my custom input component to work with vee-validate?

I have created an input component in my Vue project and I'm using this component inside my forms. I wanna use vee-validate to validate my inputs.

First, I tried to validate my component just like a normal input, but after failing at displaying error messages and checking Vue Devtools, I figured out that errors are scoped to the input component. (although there are two computed properties named "errors" and "fields" that all my components have access to, from root to input component itself.)

Then I checked vee-validate docs and tried to use Validation Provider and Validation Observer. These two components are too confusing to me and I couldn't figure out how to use them even after checking this medium article. I don't know how these components are making use of scoped slots.

I wanna be able to validate input components in the parent and also display their error messages in the parent above the form. (with or without Validation Observer and/or Validation Provider). Any approach is appreciated.

This is my input component:

<template>
  <div class="info-input-control">
    <div class="info-input-icon">
      <slot name="icon"><span uk-icon="icon: pencil; ratio: 1.4"></span></slot>
    </div>

    <input :id="id" :type="type" :value="value" :name="name"
           v-validate="validations || ''"
           @keyup.enter="$emit('enter')"
           @focus="isActive = true"
           @blur="isActive = value.length > 0"
           @input="$emit('input', $event.target.value)" :key="name">
    <label :for="id" :class="{'info-input-label': true, 'is-active': isActive}">{{label}}</label>

  </div>
</template>

<script>
    export default {
        name: 'InfoTextInput',
        props: {
            id: String,
            label: String,
            ltr: Boolean,
            name: {
                type: String,
                required: true
            },
            type: {
                type: String,
                required: true,
                validator: function (value) {
                    return ['text', 'password', 'email'].indexOf(value) !== -1
                }
            },
            validations: Object,
            value: String
        },
        data() {
            return {
                isActive: false
            }
        },
        mounted() {
            this.isActive = this.value.length > 0
        }
    }
</script>

and this is minimal version of my form with just one input component:

<form action="#" @submit.prevent="userLogin">
    <div class="uk-text-danger">
        <span>Errors: </span>
        <span>{{errors.first('mobile')}}</span>
    </div>

    <div class="uk-flex uk-flex-center">
        <div class="uk-width-medium">
            <info-text-input type="text" id="user-login-phone" label="Mobile" name="mobile" ltr v-model="login.mobile" :validations="mobileValidations" key="login-mobile">
                <template v-slot:icon>
                    <span uk-icon="icon: phone; ratio: 1.4"></span>
                </template>
            </info-text-input>
        </div>
    </div>
</form>

P.S: I have registered Validation Observer and Validation Provider globally.

Answers:

Answer

Try following this example:

<template>
  <div class="info-input-control">
    <div class="info-input-icon">
      <slot name="icon"><span uk-icon="icon: pencil; ratio: 1.4"></span></slot>
    </div>

    <input :id="id" :type="type" :name="name"
           v-model="localValue"
           v-validate="validations || ''"
           @keyup.enter="$emit('enter')"
           @focus="isActive = true"
           @blur="isActive = value.length > 0"
           @input="$emit('input', localValue)" :key="name">
    <label :for="id" :class="{'info-input-label': true, 'is-active': isActive}">{{label}}</label>

  </div>
</template>

<script>
    export default {
        $_veeValidate: {
           value() {
               return this.localValue;
           },
           name() {
              return this.name;
           }
        },
        name: 'InfoTextInput',
        props: {
            id: String,
            label: String,
            ltr: Boolean,
            name: {
                type: String,
                required: true
            },
            type: {
                type: String,
                required: true,
                validator: function (value) {
                    return ['text', 'password', 'email'].indexOf(value) !== -1
                }
            },
            validations: Object,
            value: String
        },
        data() {
            return {
                localValue: this.value,
                isActive: false
            }
        },
        mounted() {
            this.isActive = this.localValue.length > 0
        }
    }
</script>

I added only a couple things - the $_veeValidate section in the script. I also changed your component to use the value prop but store the changes in a local reactive item called localValue. Generally you won't want to change the value of a prop, although it might work in this simple of a case. Re-read One-Way Data Flow for details.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.