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.

Categories
Vue 3

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

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.

Initial Errors

We can initialize the form with error messages.

For example, we can write:

<template>
  <Form :initial-errors="initialErrors" :validation-schema="schema">
    <Field name="email" as="input" />
    <ErrorMessage name="email" />
    <br />

    <Field name="name" as="input" type="text" />
    <br />

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

    <button type="submit">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({
      email: yup.string().required().email(),
      name: yup.string().required(),
      password: yup.string().required().min(8),
    });

    return {
      schema,
      initialErrors: {
        email: "This email is already taken",
        password: "The password is too short",
      },
    };
  },
};
</script>

We set the initial-errors prop to the initialErrors reactive property.

It has the field names and the corresponding error messages.

Then we display them with the ErrorMessage component.

This is handy for displaying error messages retrieved from the server-side.

Setting Errors Manually

We can also set errors manually with Vee-Validate 4.

We can set the error message for one field with the setFieldError method.

And we can set error message for more than one field with the setErrors method.

For example, we can write:

<template>
  <Form v-slot="{ setFieldError, setErrors }">
    <Field name="email" as="input" />
    <ErrorMessage name="email" />
    <br />

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

    <button type="button" @click="setFieldError('email', 'nope')">
      Set Single Error
    </button>
    <button
      type="button"
      @click="setErrors({ email: 'nope', password: 'wrong' })"
    >
      Set Multiple Errors
    </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({
      email: yup.string().required().email(),
      name: yup.string().required(),
      password: yup.string().required().min(8),
    });

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

to access the setFieldError and setError methods from the slot props of the Form component.

Then we call them in the click handlers of the buttons.

setFieldError takes the form field name and error message as the arguments.

And setErrors takes an object with the form field names as the keys and the error messages as the corresponding values respectively.

We can call the same methods in the submit handler.

To do this, we write:

<template>
  <Form @submit="onSubmit">
    <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, actions) {
      actions.setFieldError("email", "this email is already taken");
      actions.setErrors({
        email: "this field is already taken",
        password: "someone already has this password",
      });
    },
  },
};
</script>

We call the same methods from the actions parameter of the onSubmit submit handler.

Conclusion

We can set form validation error messages manually in our Vue 3 app with Vee-Validate 4.