Categories
Vue 3

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

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.

Use Predefined Rules

We can use predefined validation rules from the @vee-validate/rules package.

To install the package, we run:

yarn add @vee-validate/rules

or:

npm install @vee-validate/rules

Then we can add the rules from the package with the defineRule function by writing:

<template>
  <Form @submit="submit" :validation-schema="schema" v-slot="{ errors }">
    <Field name="email" as="input" />
    <span>{{ errors.email }}</span>
    <br />
    <Field name="password" as="input" type="password" />
    <span>{{ errors.password }}</span>
    <br />
    <button>Submit</button>
  </Form>
</template>
<script>
import { Form, Field, defineRule } from "vee-validate";
import { required, email, min } from "@vee-validate/rules";
defineRule("required", required);
defineRule("email", email);
defineRule("min", min);

export default {
  components: {
    Form,
    Field,
  },
  data() {
    const schema = {
      email: "required|email",
      password: "required|min:8",
    };
    return {
      schema,
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

We import the rules from the @vee-validate/rules package and then pass them into the defineRule function to use it.

Then we can use them in the schema validation schema.

In the template, we display the error messages from the errors object.

If we enter something that’s invalid, we see a generic error message displayed.

Also, we can import all rules from the package by writing:

<template>
  <Form @submit="submit" :validation-schema="schema" v-slot="{ errors }">
    <Field name="email" as="input" />
    <span>{{ errors.email }}</span>
    <br />
    <Field name="password" as="input" type="password" />
    <span>{{ errors.password }}</span>
    <br />
    <button>Submit</button>
  </Form>
</template>
<script>
import { Form, Field, defineRule } from "vee-validate";
import * as rules from "@vee-validate/rules";

Object.keys(rules).forEach((rule) => {
  defineRule(rule, rules[rule]);
});

export default {
  components: {
    Form,
    Field,
  },
  data() {
    const schema = {
      email: "required|email",
      password: "required|min:8",
    };
    return {
      schema,
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

Available Rules

There are many built-in validation provided by the @vee-validate/rules package.

alpha

The alpha rule validates that the inputted value only has alphabetic characters.

For instance, we can use it by writing:

<template>
  <Form @submit="submit" v-slot="{ errors }">
    <Field name="field" rules="alpha" />
    <span>{{ errors.field }}</span>
  </Form>
</template>
<script>
import { Form, Field, defineRule } from "vee-validate";
import * as rules from "@vee-validate/rules";

Object.keys(rules).forEach((rule) => {
  defineRule(rule, rules[rule]);
});

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

We can also set the rules prop value in object format:

<template>
  <Form @submit="submit" v-slot="{ errors }">
    <Field name="field" :rules="validations" />
    <span>{{ errors.field }}</span>
  </Form>
</template>
<script>
import { Form, Field, defineRule } from "vee-validate";
import * as rules from "@vee-validate/rules";

Object.keys(rules).forEach((rule) => {
  defineRule(rule, rules[rule]);
});

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

to add the same validation rule.

Conclusion

We can add predefined validation rules into our Vue 3 app with Vee-Validate 4.

Categories
Vue 3

Vue 3 — Directives

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create more complex directives.

Directive Arguments

We can get directive arguments by getting the value from the binding.arg property.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <p v-absolute:[direction]="50">foo</p>
    </div> <script>
      const app = Vue.createApp({
        data() {
          return {
            direction: "right"
          };
        }
      }); 

      app.directive("absolute", {
        mounted(el, binding) {
          el.style.position = "absolute";
          const s = binding.arg || "top";
          el.style[s] = `${binding.value}px`;
        }
      }); 

      app.mount("#app");
    </script>
  </body>
</html>

We create the absolute directive which has a mounted hook.

It takes a binding parameter which has the arg property with the argument value which we passed into the square brackets of the directive.

Therefore, it’ll be the direction value.

We set the property of the style with the binding.value , which is the value we passed into the directive right of the equal sign.

Also, we can make the directive’s value by passing an expression as the value of the directive.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input type="range" min="0" max="100" v-model="padding" />
      <p v-absolute:[direction]="padding">foo</p>
    </div> <script>
      const app = Vue.createApp({
        data() {
          return {
            direction: "left",
            padding: 0
          };
        }
      }); 

      app.directive("absolute", {
        mounted(el, binding) {
          el.style.position = "absolute";
          const s = binding.arg || "top";
          el.style[s] = `${binding.value}px`;
        },
        updated(el, binding) {
          const s = binding.arg || "top";
          el.style[s] = `${binding.value}px`;
        }
      }); 

      app.mount("#app");
    </script>
  </body>
</html>

We have the absolute directive with the updated hook added.

The updated hook will pick up any updates of the directive’s value .

Therefore, when we move the slider, the ‘foo’ text will move along with it.

Function Shorthand

We can shorten our directive definition with the function shorthand.

If we only have the mounted and updated hooks in our directive, then we can use it.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input type="range" min="0" max="100" v-model="padding" />
      <p v-absolute:[direction]="padding">foo</p>
    </div> <script>
      const app = Vue.createApp({
        data() {
          return {
            direction: "left",
            padding: 0
          };
        }
      }); 

      app.directive("absolute", (el, binding) => {
        el.style.position = "absolute";
        const s = binding.arg || "top";
        el.style[s] = `${binding.value}px`;
      }); 

      app.mount("#app");
    </script>
  </body>
</html>

We shortened our absolute directive to include a callback instead of an object with the hooks.

It does the same things like the one in the previous example since we only have the mounted and update hooks in it.

This is a handy shorthand to avoid repeating code.

Object Literals

If we need multiple values in our directive, we can pass in an object literal to it.

Then binding.value has the object we pass in.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <p v-custom-text="{ color: 'green', text: 'hello!' }"></p>
    </div> <script>
      const app = Vue.createApp({}); 
      app.directive("custom-text", (el, binding) => {
        const { color, text } = binding.value;
        el.style.color = color;
        el.textContent = text;
      }); 

      app.mount("#app");
    </script>
  </body>
</html>

to create a custom-text directive that takes an object.

We get the color and text properties of the object from binding.value .

Conclusion

We can create directives easier with some shorthands.

Also, directives can take arguments and values.

Categories
Vue 3

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

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.

Rules with Multiple Parameters

We can have global validation rules with multiple parameters.

To define and use them, we write:

<template>
  <Form @submit="onSubmit">
    <Field name="number" as="input" rules="required|min:0,100" />
    <ErrorMessage name="number" />
    <br />

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

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

defineRule("required", (value) => {
  if (!value || !value.length) {
    return "This field is required";
  }
  return true;
});

defineRule("min", (value, [min, max]) => {
  if (!value || !value.length) {
    return true;
  }
  const numericValue = +value;
  if (numericValue < min) {
    return `This field must be greater than ${min}`;
  }
  if (numericValue > max) {
    return `This field must be less than ${max}`;
  }
  return true;
});

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

We define the min rule with the min and max parameters.

And we check against them to return the right validation result.

true means the value is valid.

Otherwise, we return an error message string.

Then in the rules prop, we combine the required rule with the min rule.

Schema Validation

We can add the rules to the validation schema.

For example, we can write:

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

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

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

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

defineRule("required", (value) => {
  if (!value || !value.length) {
    return "This field is required";
  }
  return true;
});

defineRule("email", (value) => {
  if (!value || !value.length) {
    return true;
  }
  if (!/[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}/.test(value)) {
    return "This field must be a valid email";
  }
  return true;
});

defineRule("minLength", (value, [limit]) => {
  if (!value || !value.length) {
    return true;
  }
  if (value.length < limit) {
    return `This field must be at least ${limit} characters`;
  }
  return true;
});

export default {
  components: {
    Form,
    Field,
  },
  data() {
    const schema = {
      email: "required|email",
      password: "required|minLength:8",
    };

    return {
      schema,
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

We defined the required , email , and minLength rules.

Then we put them all in the schema object.

Then we set the validation-schema prop to the schema reactive property.

And then we display the error messages by getting the errors object from the slot props of the Form component.

Now when we use the form, the validation rules will be used to validate the form.

Conclusion

We can validate more complex forms with in our Vue 3 app with Vee-Validate 4.

Categories
Vue 3

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

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.

Global Validators

We can define our own validation rules ith the defineRule method.

For example, we can use it by writing:

<template>
  <Form @submit="onSubmit">
    <Field name="name" as="input" rules="required" />
    <ErrorMessage name="name" />
    <br />

    <Field name="email" as="input" rules="required|email" />
    <ErrorMessage name="email" />
    <br />

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

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

defineRule("required", (value) => {
  if (!value || !value.length) {
    return "This field is required";
  }

  return true;
});

defineRule("email", (value) => {
  if (!value || !value.length) {
    return true;
  }

  if (!/[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}/.test(value)) {
    return "This field must be a valid email";
  }

  return true;
});

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

We call the defineRule method with the rule name.

The 2nd argument is the function we use to validate our form field.

value has the inputted value.

And we return true if the value is valid.

Otherwise, we return an error message string.

The rules prop of the Field component has the validation rules.

We combine multiple rules with the pipe.

Configuring Global Validators

We can also pass in arguments to our rules to configure it.

For example, we can write:

<template>
  <Form @submit="onSubmit">
    <Field name="name" as="input" rules="minLength:8" />
    <ErrorMessage name="name" />
    <br />

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

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

defineRule("minLength", (value, [limit]) => {
  if (!value || !value.length) {
    return true;
  }

  if (value.length < limit) {
    return `This field must be at least ${limit} characters`;
  }

  return true;
});

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

We defined the minLength rule with the limit variable destructured from the array.

And then we can check against that for validation.

We set the limit variable by adding the value after the colon in the rules prop.

Multiple rules with parameters can be combined by writing:

<template>
  <Form @submit="onSubmit">
    <Field name="name" as="input" rules="required|minLength:8" />
    <ErrorMessage name="name" />
    <br />

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

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

defineRule("required", (value) => {
  if (!value || !value.length) {
    return "This field is required";
  }
  return true;
});

defineRule("minLength", (value, [limit]) => {
  if (!value || !value.length) {
    return true;
  }

  if (value.length < limit) {
    return `This field must be at least ${limit} characters`;
  }

  return true;
});

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

Conclusion

We can create global validators for form fields and apply them with Vee-Validate 4 in our Vue 3 app.

Categories
Vue 3

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

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.

Call Set Form Validation Methods from Form’s ref

We can access the setFieldError and setErrors methods from the Form component’s ref.

For example, we can write:

<template>
  <Form @submit="onSubmit" ref="myForm">
    <Field name="email" as="input" />
    <ErrorMessage name="email" />
    <br />

    <Field name="password" as="input" />
    <ErrorMessage name="password" />
    <br />

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

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

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

    return {
      schema,
    };
  },
  methods: {
    onSubmit(values) {
      this.$refs.myForm.setFieldError("email", "this email is already taken");
      this.$refs.myForm.setErrors({
        email: "this field is already taken",
        password: "someone already has this password",
      });
    },
  },
};
</script>

We call setFieldError and setErrors from the form’s ref.

Validating Nested Objects

We can validate nested form objects with the Field component.

For example, we can write:

<template>
  <Form @submit="onSubmit" :validation-schema="schema">
    <Field name="links.twitter" type="url" />
    <ErrorMessage name="links.twitter" />
    <br />

    <Field name="links.github" type="url" />
    <ErrorMessage name="links.github" />
    <br />

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

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

export default {
  components: {
    Form,
    Field,
    ErrorMessage,
  },
  data() {
    const schema = yup.object().shape({
      links: yup.object().shape({
        twitter: yup.string().url(),
        github: yup.string().url(),
      }),
    });
    return {
      schema,
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

We set the name attribute to the path to the field we want to validate in the Field and ErrorMessage components.

In the schema object, we do the same thing.

We call yup.object().shape() and add the links property inside the object we pass in as the argument.

Validating Nested Arrays

We can also use Vee-Validate 4 to validate nested arrays.

For instance, we can write:

<template>
  <Form @submit="onSubmit" :validation-schema="schema">
    <Field name="links[0]" type="url" />
    <ErrorMessage name="links[0]" />
    <br />

    <Field name="links[1]" type="url" />
    <ErrorMessage name="links[1]" />
    <br />

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

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

export default {
  components: {
    Form,
    Field,
    ErrorMessage,
  },
  data() {
    const schema = yup.object().shape({
      links: yup.array().of(yup.string().url()),
    });
    return {
      schema,
    };
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
  },
};
</script>

to add the array validation.

We can check if an array of strings is a URL by writing:

yup.array().of(yup.string().url())

Composition API

We can add validation to our Vue 3 components if we use the composition API.

To do this, we write:

<template>
  <Form @submit="onSubmit" v-slot="{ errors }" :validation-schema="schema">
    <Field name="user.name" as="input" />
    <span id="nameErr">{{ errors["user.name"] }}</span>
    <br />

    <Field name="user.addresses[0]" as="input" id="address" />
    <span id="addrErr">{{ errors["user.addresses[0]"] }}</span>
    <br />

    <button id="submit">Submit</button>
  </Form>
</template>

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

export default {
  components: {
    Form,
    Field,
  },
  setup() {
    return {
      schema: yup.object({
        user: yup.object({
          name: yup.string().required(),
          addresses: yup.array().of(yup.string().required()),
        }),
      }),
      onSubmit(values) {
        console.log(values);
      },
    };
  },
};
</script>

The setUp method returns the schema reactive property and the onSubmit method.

Conclusion

We can validate forms with the composition API and validate objects and nested arrays.