DEV Community

Cover image for Issue using useForm() with a Modal | Vue 3 | Vee Validate 4
nickap
nickap

Posted on • Edited on

Issue using useForm() with a Modal | Vue 3 | Vee Validate 4

Ever used vee-validate? It is a nice form validation library and now with the help of composition API got even stronger.

Purpose of this article

Using useForm() to initialize a form that exist in a modal, the validation will run on initialization.
Thus, your form will be invalid since your fields are not existing (modal is closed). So, when you open the modal, you will get back your errors attached in your form.

A possible solution

If you faced this problem, you might think of using Form Component to register your form instead of useForm() (Composition API).
But this is a kinda cheap solution.
By not using composition API, you don't make use of the full potential of this library.

So, the best solution

Is to reset the form when the modal gets opened.

Let's see it in action

We will create a custom input element that we aim to use throughout our application.
Notice the 'undefined' we pass in useField() instead of our validation rules. This is because we want to validate at form-level and not at field-level.

The classes used are from the tailwind library, and you can have a better look in this example repo

<template>
  <div class="w-full">
    <label class="label" :for="$attrs.id">{{ label }}</label>
    <input v-model="value" class="input" v-bind="$attrs">
    <div class="mt-1 text-xs text-red-500">
      <span class="text-transparent">.</span>
      <span>
        {{ errorMessage }}
      </span>
    </div>
  </div>
</template>

<script setup>
import { toRef } from 'vue';
import { useField } from 'vee-validate';
const props = defineProps({
  name: { type: String, required: true },
  label: {
    type: String,
    required: true
  },
  // whenever we use v-model, modelValue prop passed to the child
  // https://v3-migration.vuejs.org/breaking-changes/v-model.html#_3-x-syntax
  modelValue: [String, Number]
});
// Need 'toRef' to achieve reactivity with vee-validate
const name = toRef(props, 'name');
const modelValue = toRef(props, 'modelValue');
// undefined for rules because we validate on Form-Level
const { value, errorMessage } = useField(name, undefined, {
  initialValue: modelValue
});
</script>

<style></style>
Enter fullscreen mode Exit fullscreen mode

We will use the described input component in our modal here:

<template>
  <div v-if="open">
    <!-- Form -->
    <form class="mt-5" @submit="submitFotm">
      <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
        <div class="col-span-full">
          <FormInput
            id="name"
            v-model="author.name"
            name="name"
            type="text"
            label="Name"
            placeholder="Author's Name"
          />
        </div>
      </div>
      <!-- Form buttons -->
      <div class="flex items-center justify-end space-x-3 mt-7">
        <button type="button" class="px-4 text-sm bg-gray-100 py-2.5 rounded-md" @click="closeModal()">
          Cancel
        </button>
        <button type="submit" class="btn submit" :disabled="!meta.dirty">
          {{ author._id ? "Update" : "Create" }}
        </button>
      </div>
    </form>
  </div>
</template>

<script setup>
import { useForm } from 'vee-validate';
const schema = {
  name: 'required|min:3'
};

// Get function used to handle form submission and set validation schema
const { handleSubmit, meta, resetForm } = useForm({
  validationSchema: schema
});

const onInvalidSubmit = () => {
  // Form is invalid
  // U can shake the submit button and inform the user in your way
};

const submitFotm = handleSubmit((values) => {
  // Handle submision and close the modal.
  // Also, pass onInvalidSubmit() as a  callback function.
  console.log(values);
  closeModal();
}, onInvalidSubmit);

const open = ref(false);
const openModal = () => {
  // The Form will get registerd and validated on setup function.
  // So it will be invalid before we open the modal.
  // We have to reset it here.
  resetForm();
  open.value = true;
};

const closeModal = () => {
  open.value = false;
};

// We are exposing openModal() and closeModal()
// So we can handle the visibility on the parrent component
defineExpose({
  openModal,
  closeModal
});
</script>

<style></style>

Enter fullscreen mode Exit fullscreen mode

So, we got access to the resetForm() helper and used it to reset the form when we show the modal.

You can have a better look in this minimal nuxt repo.

Hope you find this info helpful.

Thanks for reading this!

Top comments (2)

Collapse
 
jaredm profile image
professorjrod

Good job, I could follow along and I've never used Vue.

One tip I have is that you can highlight syntax in your code blocks by writing the language name next to the backticks.

```javascript
Enter fullscreen mode Exit fullscreen mode

like so

console.log("Hello world!")
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nickap profile image
nickap

Thanks for your interest @jaredm