Categories
Vue 3

Form Validation in a Vue 3 App with Vee-Validate 4 — Yup Schemas, Radio Buttons, and Checkboxes

Form validation is an important part of any app.

In this article, we’ll look at how to use Vee-Validate 4 in our Vue 3 app for form validation.

Yup Schemas in Data

Making our validation schema reactive is an unnecessary overhead that we can eliminate.

To do this, we can either add the schema with the setUp method or call markRaw with our schema to make it non-reactive.

For example, we can write:

<template>
  <Form v-slot="{ errors }" :validation-schema="schema">
    <Field as="input" name="email" />
    <span>{{ errors.email }}</span>
    <br />

    <Field as="input" name="password" type="password" />
    <span>{{ errors.password }}</span>
    <br />

    <button>Submit</button>
  </Form>
</template>

<script>
import { Form, Field } from "vee-validate";
import * as yup from "yup";

export default {
  components: {
    Form,
    Field,
  },
  setup() {
    const schema = yup.object().shape({
      email: yup.string().required().email(),
      password: yup.string().required().min(8),
    });

    return {
      schema,
    };
  },
};
</script>

to create our schema in the setUp method.

We just return the object returned by Yup so the schema property won’t be reactive.

To call markRaw , we write:

<template>
  <Form v-slot="{ errors }" :validation-schema="schema">
    <Field as="input" name="email" />
    <span>{{ errors.email }}</span>
    <br />

    <Field as="input" name="password" type="password" />
    <span>{{ errors.password }}</span>
    <br />

    <button>Submit</button>
  </Form>
</template>

<script>
import { markRaw } from "vue";
import { Form, Field } from "vee-validate";
import * as yup from "yup";

export default {
  components: {
    Form,
    Field,
  },
  data() {
    const schema = markRaw(
      yup.object().shape({
        email: yup.string().required().email(),
        password: yup.string().required().min(8),
      })
    );

    return {
      schema,
    };
  },
};
</script>

We call markRaw with our Yup schema to make it non-reactive.

Radio Buttons

We can use Vee-Validate 4 to validate radio buttons.

For example, we can write:

<template>
  <Form :validation-schema="schema" @submit="onSubmit">
    <Field name="drink" type="radio" value=""></Field> None
    <Field name="drink" type="radio" value="Tea"></Field> Tea
    <Field name="drink" type="radio" value="Coffee"></Field> Coffee
    <ErrorMessage name="drink" />
  </Form>
</template>

<script>
import { Form, Field, ErrorMessage } from "vee-validate";

export default {
  components: {
    Form,
    Field,
    ErrorMessage,
  },
  data() {
    return {
      schema: {
        drink: (value) => {
          if (value) {
            return true;
          }

          return "You must choose a drink";
        },
      },
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

We have the Field component with type set to radio to create our radio buttons.

Then in the data method, we return the schema reactive property with the drink method to validate our radio buttons.

value has the checked value as specified by the value prop.

We return true if the choices are valid and an error message otherwise.

And then we display the ErrorMessage component to show error messages.

Checkboxes

We can add checkbox validation with Vee-Validate 4.

For instance, we can write:

<template>
  <Form :validation-schema="schema" @submit="onSubmit">
    <Field name="drink" type="checkbox" value="Milk"></Field> Milk
    <Field name="drink" type="checkbox" value="Tea"></Field> Tea
    <Field name="drink" type="checkbox" value="Coffee"></Field> Coffee
    <ErrorMessage name="drink" />
  </Form>
</template>

<script>
import { Form, Field, ErrorMessage } from "vee-validate";

export default {
  components: {
    Form,
    Field,
    ErrorMessage,
  },
  data() {
    return {
      schema: {
        drink: (value) => {
          if (Array.isArray(value) && value.length) {
            return true;
          }
          return "You must choose a drink";
        },
      },
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

to add validation for checkboxes. We set the type prop to checkbox to render a checkbox.

And the drink method has all the checked values in an array if any checkbox is checked.

We return true if the choices are valid and an error message otherwise.

And then we display the ErrorMessage component to show error messages.

Conclusion

We can make Yup schemas non-reactive to reduce overhead and validate radio buttons and checkboxes with Vee-Validate 4 in our Vue 3 app.

Categories
Vue 3

Form Validation in a Vue 3 App with Vee-Validate 4 — Dynamic Forms and App Bundle Size

Form validation is an important part of any app.

In this article, we’ll look at how to use Vee-Validate 4 in our Vue 3 app for form validation.

Dynamic Form with Initial Values

We can set initial values for dynamic forms as we do with static forms.

To do this, we pass in an object with the values into the initial-values prop:

<template>
  <Form
    v-slot="{ errors }"
    :validation-schema="schema.validation"
    :initial-values="schema.values"
  >
    <div v-for="field in schema.fields" :key="field.name">
      <label :for="field.name">{{ field.label }}</label>
      <Field :as="field.as" :id="field.name" :name="field.name" />
      <span>{{ errors[field.name] }}</span>
    </div>

    <button>Submit</button>
  </Form>
</template>

<script>
import { Form, Field } from "vee-validate";
import * as yup from "yup";

export default {
  name: "DynamicForm",
  components: {
    Form,
    Field,
  },
  data() {
    return {
      schema: {
        fields: [
          {
            label: "Name",
            name: "name",
            as: "input",
          },
          {
            label: "Email",
            name: "email",
            as: "input",
          },
          {
            label: "Password",
            name: "password",
            as: "input",
          },
        ],
        validation: yup.object().shape({
          email: yup.string().email().required(),
          name: yup.string().required(),
          password: yup.string().min(8).required(),
        }),
        values: {
          name: "james",
          email: "abc@abc.com",
          password: "password",
        },
      },
    };
  },
};
</script>

We add the values property with the values into the schema reactive property.

Then we pass that into the validation-schema prop.

The property names of schema.values should match the values of the name prop of the field we want to fill in.

Now we should see the initial form values populated.

Reducing App Bundle Size

To reduce the bundle size of our app, we can selectively import the parts of the yup module we require.

For example, instead of writing:

<template>
  <Form
    v-slot="{ errors }"
    :validation-schema="schema.validation"
    :initial-values="schema.values"
  >
    <div v-for="field in schema.fields" :key="field.name">
      <label :for="field.name">{{ field.label }}</label>
      <Field :as="field.as" :id="field.name" :name="field.name" />
      <span>{{ errors[field.name] }}</span>
    </div>

    <button>Submit</button>
  </Form>
</template>

<script>
import { Form, Field } from "vee-validate";
import * as yup from "yup";

export default {
  name: "DynamicForm",
  components: {
    Form,
    Field,
  },
  data() {
    return {
      schema: {
        fields: [
          {
            label: "Name",
            name: "name",
            as: "input",
          },
          {
            label: "Email",
            name: "email",
            as: "input",
          },
          {
            label: "Password",
            name: "password",
            as: "input",
          },
        ],
        validation: yup.object().shape({
          email: yup.string().email().required(),
          name: yup.string().required(),
          password: yup.string().min(8).required(),
        }),
      },
    };
  },
};
</script>

We can write:

<template>
  <Form
    v-slot="{ errors }"
    :validation-schema="schema.validation"
    :initial-values="schema.values"
  >
    <div v-for="field in schema.fields" :key="field.name">
      <label :for="field.name">{{ field.label }}</label>
      <Field :as="field.as" :id="field.name" :name="field.name" />
      <span>{{ errors[field.name] }}</span>
    </div>

    <button>Submit</button>
  </Form>
</template>

<script>
import { Form, Field } from "vee-validate";
import { object, string } from "yup";

export default {
  name: "DynamicForm",
  components: {
    Form,
    Field,
  },
  data() {
    return {
      schema: {
        fields: [
          {
            label: "Name",
            name: "name",
            as: "input",
          },
          {
            label: "Email",
            name: "email",
            as: "input",
          },
          {
            label: "Password",
            name: "password",
            as: "input",
          },
        ],
        validation: object().shape({
          email: string().email().required(),
          name: string().required(),
          password: string().min(8).required(),
        }),
      },
    };
  },
};
</script>

We just import the object and string methods form the yup module, then we call them to create our schema.

Conclusion

We can add dynamic forms with initial values easily into our Vue 3 dynamic forms with Vee-Validate 4.

Also, we can reduce bundle size by selectively importing items from the yup module.

Categories
Vue 3

Form Validation in a Vue 3 App with Vee-Validate 4 — useForm and Dynamic Forms

Form validation is an important part of any app.

In this article, we’ll look at how to use Vee-Validate 4 in our Vue 3 app for form validation.

useForm()

The useForm() function lets us create form context that we can use to create reactive forms.

To use it, we write:

<template>
  <form @submit="onSubmit">
    <Field name="email" />
    <span>{{ errors.email }}</span>
    <br />

    <Field name="password" />
    <span>{{ errors.password }}</span>
    <br />

    <input type="submit" />
  </form>
</template>

<script>
import { Field, useForm } from "vee-validate";
import * as yup from "yup";

export default {
  components: {
    Field,
  },
  setup() {
    const { handleSubmit, errors } = useForm({
      validationSchema: yup.object().shape({
        email: yup.string().required().email(),
        password: yup.string().required().min(3),
      }),
    });
    const onSubmit = handleSubmit((values, actions) => {
      alert(JSON.stringify(values, null, 2));
      actions.resetForm();
    });

    return {
      errors,
      onSubmit,
    };
  },
};
</script>

We call the useForm function with the validationSchema property to set the validation schema.

email and password are the name attribute values of the fields.

It returns an object with the handleSubmit function and the errors object with the form validation errors.

We use errors in the form.

And we call handleSubmit with a callback to handle submissions.

values has the inputted values.

actions has the resetForm method to reset the validation status of the form.

We can set the initial values of the form fields.

To do this, we write:

<template>
  <form @submit="onSubmit">
    <Field name="email" />
    <span>{{ errors.email }}</span>
    <br />

    <Field name="password" />
    <span>{{ errors.password }}</span>
    <br />

    <input type="submit" />
  </form>
</template>

<script>
import { Field, useForm } from "vee-validate";
import * as yup from "yup";

export default {
  components: {
    Field,
  },
  setup() {
    const { handleSubmit, errors } = useForm({
      initialValues: {
        email: "abc@abc.com",
        password: "",
      },
      validationSchema: yup.object().shape({
        email: yup.string().required().email(),
        password: yup.string().required().min(3),
      }),
    });
    const onSubmit = handleSubmit((values, actions) => {
      alert(JSON.stringify(values, null, 2));
      actions.resetForm();
    });

    return {
      errors,
      onSubmit,
    };
  },
};
</script>

to add the initialValues property into the object we pass into useForm to set the value of the email field.

Dynamic Form

We can use Vee-Validate 4 to validate dynamically rendered forms.

For example, we can write:

<template>
  <Form v-slot="{ errors }" :validation-schema="schema.validation">
    <div v-for="field in schema.fields" :key="field.name">
      <label :for="field.name">{{ field.label }}</label>
      <Field :as="field.as" :id="field.name" :name="field.name" />
      <span>{{ errors[field.name] }}</span>
    </div>

    <button>Submit</button>
  </Form>
</template>

<script>
import { Form, Field } from "vee-validate";
import * as yup from "yup";

export default {
  components: {
    Form,
    Field,
  },
  data() {
    return {
      schema: {
        fields: [
          {
            label: "Name",
            name: "name",
            as: "input",
          },
          {
            label: "Email",
            name: "email",
            as: "input",
          },
          {
            label: "Password",
            name: "password",
            as: "input",
          },
        ],
        validation: yup.object().shape({
          email: yup.string().email().required(),
          name: yup.string().required(),
          password: yup.string().min(8).required(),
        }),
      },
    };
  },
};
</script>

In the data method, we return an object with the schema reactive property.

It has the data to render the fields in the fields array.

We use it to render the label and name and what to render the Field as with the as property.

The validation property has the validation schema for our fields.

In the template, we render the items and display the errors in the span .

This is more convenient than adding each field into our template.

Conclusion

We can create forms with the useForm function.

And we can create dynamic forms with validation in our Vue 3 app with Vee-Validate 4.

Categories
Vue 3

Form Validation in a Vue 3 App with Vee-Validate 4 — Validation Messages and Composition API

Form validation is an important part of any app.

In this article, we’ll look at how to use Vee-Validate 4 in our Vue 3 app for form validation.

Validation Messages Placeholder Interpolation

We can add placeholders into our validation messages and interpolate them when displaying the messages.

For instance, we can write:

<template>
  <Form @submit="onSubmit" v-slot="{ errors }">
    <Field name="name" rules="between:0,10" />
    <span>{{ errors.name }}</span>
  </Form>
</template>

<script>
import { Form, Field, defineRule, configure } from "vee-validate";
import { between } from "@vee-validate/rules";
import { localize } from "@vee-validate/i18n";

defineRule("between", between);

configure({
  generateMessage: localize("en", {
    messages: {
      between: "The {field} value must be between 0:{min} and 1:{max}",
    },
  }),
});

export default {
  components: {
    Form,
    Field,
  },
  data() {
    return {
      validations: { size: 100 },
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

{field} , 0:{min} , and 1:{max} are the placeholders in our validation message string.

0:{min} and 1:{max} are the rule arguments.

{field} is the rule name.

Composition API

Vee-Validate 4 comes with hooks for the Composition API.

For example, we can write:

<template>
  <div>
    <input type="text" v-model="value" />
    <span>{{ errorMessage }}</span>
  </div>
</template>

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

export default {
  setup() {
    function validate(value) {
      if (!value) {
        return "This field is required";
      }

      if (value.length < 3) {
        return "Must contain more than 3 characters";
      }

      return true;
    }

    const { value, errorMessage } = useField("fieldName", validate);

    return {
      value,
      errorMessage,
    };
  },
};
</script>

We call the useField function with the field name as the first argument.

And the 2nd argument is the function we use for validation.

Returning true means that the inputted value, which is the value parameter, is valid.

We return the form validation error message if it’s not valid.

useField returns the errorMessage that we display in the template.

We can also use the yup library for validation.

For instance, we can write:

<template>
  <div>
    <input type="text" v-model="value" />
    <span>{{ errorMessage }}</span>
  </div>
</template>

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

export default {
  setup() {
    const { value, errorMessage } = useField(
      "fieldName",
      yup.string().required().min(3)
    );

    return {
      value,
      errorMessage,
    };
  },
};
</script>

to make sure that the entered value has at least 3 characters.

We can also use global validators.

For example, we can write:

<template>
  <div>
    <input type="text" v-model="value" />
    <span>{{ errorMessage }}</span>
  </div>
</template>

<script>
import { defineRule, useField } from "vee-validate";
import * as rules from "@vee-validate/rules";
Object.keys(rules).forEach((rule) => {
  defineRule(rule, rules[rule]);
});

export default {
  setup() {
    const { value, errorMessage } = useField("fieldName", "required|min:3");

    return {
      value,
      errorMessage,
    };
  },
};
</script>

to add the rule with the built-in validators.

We have to call defineRule to add the rules first.

Conclusion

We can customize validation messages with Vee-Validate 4.

Also, we can use the hooks with the Composition API to add validation to our Vue 3 app.

Categories
Vue 3

Form Validation in a Vue 3 App with Vee-Validate 4 — Validation Messages

Form validation is an important part of any app.

In this article, we’ll look at how to use Vee-Validate 4 in our Vue 3 app for form validation.

Localized Messages

We can add validation messages for different locales with the @vee-validate/i18n package.

To install it, we run:

yarn add @vee-validate/i18n

or:

npm install @vee-validate/i18n

to install the package.

Then we can use it by writing:

<template>
  <Form @submit="onSubmit" v-slot="{ errors }">
    <Field name="name" rules="required" />
    <span>{{ errors.name }}</span>
  </Form>
</template>

<script>
import { Form, Field, defineRule, configure } from "vee-validate";
import { required } from "@vee-validate/rules";
import { localize } from "@vee-validate/i18n";

defineRule("required", required);

configure({
  generateMessage: localize("en", {
    messages: {
      required: "This field is required",
    },
  }),
});

export default {
  components: {
    Form,
    Field,
  },
  data() {
    return {
      validations: { size: 100 },
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

We call defineRule to add the required rule.

Then we call configure with an object with the generateMessage property.

We call localize with the locale string and the messages to add the English language message for the required rule.

Also, we can add messages for multiple locales using the localize function:

<template>
  <Form @submit="onSubmit" v-slot="{ errors }">
    <Field name="name" rules="required" />
    <span>{{ errors.name }}</span>
  </Form>
</template>

<script>
import { Form, Field, defineRule, configure } from "vee-validate";
import { required } from "@vee-validate/rules";
import { localize, setLocale } from "@vee-validate/i18n";

defineRule("required", required);

configure({
  generateMessage: localize({
    en: {
      messages: {
        required: "This field is required",
      },
    },
    fr: {
      messages: {
        required: "Ce champ est requis",
      },
    },
  }),
});

setLocale("fr");

export default {
  components: {
    Form,
    Field,
  },
  data() {
    return {
      validations: { size: 100 },
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

We add an object with the locale names as the keys and the messages inside it.

Then we call setLocale to set the locale.

We have the required property with the message for each property to set the message for the required rule.

Now the French validation error messages will be shown.

Also, we can add built-in messages for the rules.

For instance, we can write:

<template>
  <Form @submit="onSubmit" v-slot="{ errors }">
    <Field name="name" rules="required" />
    <span>{{ errors.name }}</span>
  </Form>
</template>

<script>
import { Form, Field, defineRule, configure } from "vee-validate";
import { required } from "@vee-validate/rules";
import { localize, setLocale } from "@vee-validate/i18n";
import en from "@vee-validate/i18n/dist/locale/en.json";
import fr from "@vee-validate/i18n/dist/locale/fr.json";

defineRule("required", required);

configure({
  generateMessage: localize({
    en,
    fr,
  }),
});

setLocale("fr");

export default {
  components: {
    Form,
    Field,
  },
  data() {
    return {
      validations: { size: 100 },
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

to import the validation messages provided by Vee-Validate 4.

Then we pass them into the object we pass into the localize function.

Conclusion

We can add custom validation messages with Vee-Validate 4 in our Vue 3 app.