how can I use custom component vee-validate with v-model?
Asked Answered
D

2

5

I have a component called TextInput. I need to send v-model with @input event in this component, but I also want it to do validation with vee-validate.

But when I use the handleChange function in the usefield, it does the validation. But this time I cannot send the value with the v-model.

When I do it as follows, I can use the v-model when I use the v-model in the component I call, but it does not do the validation process.

<template>
  <div
      class="TextInput"
      :class="{ 'has-error': !!errorMessage, success: meta.valid }"
  >
    <label :for="name">{{ label }}</label>
    <input
        :name="name"
        :type="type"
        :id="name"
        :value="modelValue"
        :placeholder="placeholder"
        @input="handleChange"

        @blur="handleBlur"
        v-bind="$attrs"
    />

    <p class="help-message" v-show="errorMessage || meta.valid">
      {{ errorMessage || successMessage }}
    </p>
  </div>
</template>

<script>
import { useField } from "vee-validate";

export default {
  props: {
    type: {
      type: String,
      default: "text",
    },
    modelValue:String,
    value: {
      type: String,
      default: "",
    },
    name: {
      type: String,
      required: true,
    },
    label: {
      type: String,
      required: true,
    },
    successMessage: {
      type: String,
      default: "",
    },
    placeholder: {
      type: String,
      default: "",
    },
  },
  emits: ['update:modelValue'],
  setup(props,{emit}) {

    const handleChange = (event) =>{
      emit('update:modelValue', event.target.value);
    }
    const {

      errorMessage,
      handleBlur,
      meta,
    } = useField(props.name, undefined, {
      initialValue: props.value,
    });

    return {
      handleChange,
      handleBlur,
      errorMessage,
      meta,
    };
  },
};
</script>

When you do it as below, it doesn't send the v-model, but it does the validation correctly.

<template>
  <div
    class="TextInput"
    :class="{ 'has-error': !!errorMessage, success: meta.valid }"
  >
    <label :for="name">{{ label }}</label>
    <input
      :name="name"
      :id="name"
      :type="type"
      :value="inputValue"
      :placeholder="placeholder"
      @input="handleChange"
      @blur="handleBlur"
    />

    <p class="help-message" v-show="errorMessage || meta.valid">
      {{ errorMessage || successMessage }}
    </p>
  </div>
</template>

<script>
import { useField } from "vee-validate";

export default {
  props: {
    type: {
      type: String,
      default: "text",
    },
    value: {
      type: String,
      default: "",
    },
    name: {
      type: String,
      required: true,
    },
    label: {
      type: String,
      required: true,
    },
    successMessage: {
      type: String,
      default: "",
    },
    placeholder: {
      type: String,
      default: "",
    },
  },
  setup(props) {
    const {
      value: inputValue,
      errorMessage,
      handleBlur,
      handleChange,
      meta,
    } = useField(props.name, undefined, {
      initialValue: props.value,
    });

    return {
      handleChange,
      handleBlur,
      errorMessage,
      inputValue,
      meta,
    };
  },
};
</script>

How can I send both validation and v-model in the same way?

Distortion answered 4/2, 2022 at 7:18 Comment(0)
L
5

You need to add the valueProp parameter to the useField constructor and change the prop value to modelValue. You also need to introduce a handler for the input event that will call both handleChange from useField and also emit the proper update:modelValue event so that the v-model binding will work.

This will allow for both validation and the v-model binding to work. I've made the example changes below:

<template>
  <div
    class="TextInput"
    :class="{ 'has-error': !!errorMessage, success: meta.valid }"
  >
    <label :for="name">{{ label }}</label>
    <input
      :name="name"
      :id="name"
      :type="type"
      :value="inputValue"
      :placeholder="placeholder"
      @input="onInput($event)"
      @blur="handleBlur"
    />

    <p class="help-message" v-show="errorMessage || meta.valid">
      {{ errorMessage || successMessage }}
    </p>
  </div>
</template>

<script>
import { useField } from "vee-validate";

export default {
  props: {
    type: {
      type: String,
      default: "text",
    },
    modelValue: {
      type: String,
      default: "",
    },
    name: {
      type: String,
      required: true,
    },
    label: {
      type: String,
      required: true,
    },
    successMessage: {
      type: String,
      default: "",
    },
    placeholder: {
      type: String,
      default: "",
    },
  },
  setup(props, {emit}) {
    const {
      value: inputValue,
      errorMessage,
      handleBlur,
      handleChange,
      meta,
    } = useField(props.name, undefined, {
      initialValue: props.modelValue,
      valueProp: props.modelValue
    });

    const onInput = (event) => {
      handleChange(event, true)
      emit('update:modelValue', event.target.value)
    }

    return {
      onInput,
      handleChange,
      handleBlur,
      errorMessage,
      inputValue,
      meta,
    };
  },
};
</script>
Laughton answered 13/4, 2022 at 19:9 Comment(3)
thank you so much :) Great solution again thank you :) sorry for the late responseDistortion
Could you please include an example on how to use it? I can't manage to trigger the validations....Aeneas
this does not seem to be working anymore as of at least v.4.11.1. useField has a new option syncVModel (see vee-validate.logaretm.com/v4/guide/composition-api/…). So the lines initialValue: props.modelValue, valueProp: props.modelValue have to be replaced with syncVModel: true,.Python
P
4

v4.11.1 of vee-validate introduced easier v-model support (see official docs here). As of this version the solution posted by Tom Malitz is not working anymore. Here is an updated solution:

<template>
  <div
    class="TextInput"
    :class="{ 'has-error': !!errorMessage, success: meta.valid }"
  >
    <label :for="name">{{ label }}</label>
    <input
      :name="name"
      :id="name"
      :type="type"
      :value="inputValue"
      :placeholder="placeholder"
      @input="handleChange"
      @blur="handleBlur"
    />

    <p class="help-message" v-show="errorMessage || meta.valid">
      {{ errorMessage || successMessage }}
    </p>
  </div>
</template>

<script>
import { useField } from "vee-validate";

export default {
  props: {
    type: {
      type: String,
      default: "text",
    },
    modelValue: {
      type: String,
      default: "",
    },
    name: {
      type: String,
      required: true,
    },
    label: {
      type: String,
      required: true,
    },
    successMessage: {
      type: String,
      default: "",
    },
    placeholder: {
      type: String,
      default: "",
    },
  },
  setup(props, {emit}) {
    const {
      value: inputValue,
      errorMessage,
      handleBlur,
      handleChange,
      meta,
    } = useField(props.name, undefined, {
      syncVModel: true
    });

    return {
      handleChange,
      handleBlur,
      errorMessage,
      inputValue,
      meta,
    };
  },
};
</script>
Python answered 9/9, 2023 at 23:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.