tutoriales.com

Validación de Formularios Reactivos en Vue 3 con VeeValidate y Yup: Guía Completa

Este tutorial te guiará a través de la implementación de validación de formularios reactivos en tus aplicaciones Vue 3 utilizando las poderosas bibliotecas VeeValidate y Yup. Aprenderás a definir esquemas de validación, mostrar mensajes de error y gestionar el estado del formulario de manera eficiente.

Intermedio20 min de lectura7 views
Reportar error

La gestión de formularios es una parte esencial de casi cualquier aplicación web. Garantizar que los datos ingresados por el usuario sean válidos es crucial para mantener la integridad de la aplicación y proporcionar una buena experiencia de usuario. En Vue 3, la combinación de VeeValidate y Yup ofrece una solución elegante y robusta para la validación de formularios reactivos.

🚀 ¿Por qué VeeValidate y Yup?

Existen varias formas de validar formularios en Vue.js, desde la validación manual hasta el uso de bibliotecas. VeeValidate se destaca por su enfoque declarativo y su profunda integración con Vue. Permite definir reglas de validación directamente en tu plantilla o mediante esquemas. Cuando se combina con Yup, una biblioteca de validación de esquemas, se obtiene una solución extremadamente potente y flexible.

VeeValidate

VeeValidate es una biblioteca de validación para Vue.js que simplifica el proceso de añadir reglas de validación a tus formularios. Ofrece componentes para campos (Field), formularios (Form) y mensajes de error (ErrorMessage), además de una Composition API que se integra perfectamente con Vue 3.

Yup

Yup es un constructor de esquemas JavaScript para analizar y validar valores. Permite definir la forma, los requisitos y las reglas de validación para objetos JavaScript de forma declarativa. Su integración con VeeValidate a través de toTypedSchema lo convierte en el compañero perfecto para la validación basada en esquemas.

💡 Consejo: Usar VeeValidate con Yup te permite centralizar tus reglas de validación y reutilizarlas en diferentes partes de tu aplicación, mejorando la coherencia y mantenibilidad.

🛠️ Configuración Inicial del Proyecto

Antes de sumergirnos en la validación, necesitamos configurar un proyecto básico de Vue 3 e instalar las dependencias necesarias.

1. Crear un Proyecto Vue 3

Si aún no tienes un proyecto Vue 3, puedes crearlo usando create-vue (la forma recomendada):

npm init vue@latest
# o
yarn create vue@latest
# o
pnpm create vue@latest

Sigue las indicaciones. Para este tutorial, puedes elegir las opciones predeterminadas o no incluir TypeScript y Pinia para simplificar, a menos que ya los uses. Navega a tu directorio de proyecto:

cd my-vue-form-app

2. Instalar VeeValidate y Yup

Ahora, instalaremos las bibliotecas principales. VeeValidate tiene una integración oficial para Yup que usaremos:

npm install vee-validate yup @vee-validate/yup
# o
yarn add vee-validate yup @vee-validate/yup
# o
pnpm add vee-validate yup @vee-validate/yup

Una vez instaladas, estamos listos para empezar a construir nuestro formulario.


📝 Creando un Formulario Básico

Vamos a crear un formulario de registro simple con campos para nombre de usuario, correo electrónico y contraseña. Empezaremos sin validación para luego añadirla progresivamente.

Crea un nuevo componente llamado RegistrationForm.vue dentro de la carpeta src/components.

<template>
  <form @submit.prevent="handleSubmit">
    <h2>Registro de Usuario</h2>

    <div class="form-group">
      <label for="username">Nombre de Usuario:</label>
      <input type="text" id="username" v-model="formData.username">
    </div>

    <div class="form-group">
      <label for="email">Correo Electrónico:</label>
      <input type="email" id="email" v-model="formData.email">
    </div>

    <div class="form-group">
      <label for="password">Contraseña:</label>
      <input type="password" id="password" v-model="formData.password">
    </div>

    <div class="form-group">
      <label for="confirmPassword">Confirmar Contraseña:</label>
      <input type="password" id="confirmPassword" v-model="formData.confirmPassword">
    </div>

    <button type="submit">Registrarse</button>
  </form>
</template>

<script setup>
import { reactive } from 'vue';

const formData = reactive({
  username: '',
  email: '',
  password: '',
  confirmPassword: ''
});

const handleSubmit = () => {
  console.log('Formulario enviado:', formData);
  alert('Formulario enviado (sin validación): ' + JSON.stringify(formData, null, 2));
  // Aquí iría la lógica para enviar el formulario a un API, etc.
};
</script>

<style scoped>
form {
  max-width: 500px;
  margin: 50px auto;
  padding: 30px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
  background-color: #fff;
}
h2 {
  text-align: center;
  color: #333;
  margin-bottom: 30px;
}
.form-group {
  margin-bottom: 20px;
}
label {
  display: block;
  margin-bottom: 8px;
  font-weight: bold;
  color: #555;
}
input[type="text"],
input[type="email"],
input[type="password"] {
  width: 100%;
  padding: 12px;
  border: 1px solid #ccc;
  border-radius: 6px;
  font-size: 16px;
  box-sizing: border-box;
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus {
  border-color: #007bff;
  outline: none;
  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
}
button {
  width: 100%;
  padding: 12px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 18px;
  font-weight: bold;
  transition: background-color 0.3s ease;
}
button:hover {
  background-color: #0056b3;
}
</style>

Ahora, importa este componente en App.vue para verlo en acción:

<template>
  <RegistrationForm />
</template>

<script setup>
import RegistrationForm from './components/RegistrationForm.vue';
</script>

<style>
/* Estilos globales */
body {
  font-family: 'Arial', sans-serif;
  background-color: #f4f7f6;
  margin: 0;
  padding: 0;
}
</style>

Verifica que el formulario se renderice correctamente en tu navegador.


✨ Integrando VeeValidate: El Componente Form

VeeValidate proporciona un componente Form que envuelve tu formulario HTML y se encarga de la gestión del estado de validación. Reemplazaremos la etiqueta <form> nativa por <Form>.

1. Actualizar RegistrationForm.vue

Modifica RegistrationForm.vue para usar Form, Field y ErrorMessage.

<template>
  <!-- 🎯 Usamos el componente Form de VeeValidate -->
  <Form @submit="handleSubmit" :validation-schema="schema">
    <h2>Registro de Usuario</h2>

    <div class="form-group">
      <label for="username">Nombre de Usuario:</label>
      <!-- 📖 Usamos el componente Field para cada campo -->
      <Field name="username" type="text" id="username" />
      <!-- ⚠️ Componente para mostrar errores -->
      <ErrorMessage name="username" class="error-message" />
    </div>

    <div class="form-group">
      <label for="email">Correo Electrónico:</label>
      <Field name="email" type="email" id="email" />
      <ErrorMessage name="email" class="error-message" />
    </div>

    <div class="form-group">
      <label for="password">Contraseña:</label>
      <Field name="password" type="password" id="password" />
      <ErrorMessage name="password" class="error-message" />
    </div>

    <div class="form-group">
      <label for="confirmPassword">Confirmar Contraseña:</label>
      <Field name="confirmPassword" type="password" id="confirmPassword" />
      <ErrorMessage name="confirmPassword" class="error-message" />
    </div>

    <button type="submit">Registrarse</button>
  </Form>
</template>

<script setup>
import { Form, Field, ErrorMessage } from 'vee-validate';
import * as yup from 'yup'; // Importamos Yup
import { toTypedSchema } from '@vee-validate/yup'; // Para integrar Yup con VeeValidate

// Definimos el esquema de validación con Yup
const schema = toTypedSchema(
  yup.object({
    username: yup.string()
      .required('El nombre de usuario es obligatorio')
      .min(3, 'El nombre de usuario debe tener al menos 3 caracteres')
      .max(20, 'El nombre de usuario no puede exceder los 20 caracteres'),
    email: yup.string()
      .required('El correo electrónico es obligatorio')
      .email('Debe ser un correo electrónico válido'),
    password: yup.string()
      .required('La contraseña es obligatoria')
      .min(6, 'La contraseña debe tener al menos 6 caracteres'),
    confirmPassword: yup.string()
      .required('Confirma tu contraseña')
      .oneOf([yup.ref('password')], 'Las contraseñas no coinciden'), // Regla para que coincida con 'password'
  })
);

// La función handleSubmit recibe el objeto de valores del formulario si la validación es exitosa
const handleSubmit = (values) => {
  console.log('Formulario enviado y validado:', values);
  alert('Registro exitoso! \n' + JSON.stringify(values, null, 2));
  // Aquí iría la lógica para enviar el formulario al servidor
};
</script>

<style scoped>
/* ... (estilos existentes) ... */
.error-message {
  color: #dc3545;
  font-size: 0.875em;
  margin-top: 5px;
  display: block;
}
input.is-invalid {
  border-color: #dc3545;
}
</style>

Explicación de los cambios:

  • import { Form, Field, ErrorMessage } from 'vee-validate';: Importamos los componentes clave de VeeValidate.
  • import * as yup from 'yup';: Importamos la biblioteca Yup.
  • import { toTypedSchema } from '@vee-validate/yup';: Esta función es crucial para adaptar los esquemas de Yup a VeeValidate.
  • <Form @submit="handleSubmit" :validation-schema="schema">: El componente Form envuelve el formulario. La prop @submit solo se ejecutará si todas las reglas de validación se cumplen. La prop :validation-schema le indica a VeeValidate qué esquema de Yup debe usar para validar.
  • <Field name="username" type="text" id="username" />: El componente Field reemplaza a <input>. Es un renderless component por defecto, lo que significa que no renderiza HTML por sí mismo, sino que expone un API. Para que funcione como un input, podemos pasarle directamente los atributos HTML. La prop name es fundamental, ya que VeeValidate la usa para vincular el campo con su respectivo valor en el esquema de validación y para el manejo del estado.
  • <ErrorMessage name="username" class="error-message" />: Este componente es el encargado de mostrar los mensajes de error asociados a un campo específico (definido por la prop name). Se renderiza solo si hay un error de validación para ese campo.
  • schema (objeto Yup): Aquí definimos todas las reglas de validación. Cada propiedad del objeto yup.object() corresponde al name de un <Field>. Por ejemplo:
    • yup.string().required('mensaje'): El campo debe ser una cadena y es obligatorio.
    • .min(3, 'mensaje'): Longitud mínima.
    • .email('mensaje'): Debe ser un formato de correo electrónico válido.
    • .oneOf([yup.ref('password')], 'mensaje'): Esta es una regla avanzada que asegura que el campo confirmPassword sea igual al valor del campo password.

2. Probando la Validación

Ahora, guarda los cambios y prueba el formulario. Intenta enviarlo vacío, luego con datos incorrectos y finalmente con datos válidos. Verás los mensajes de error aparecer y desaparecer dinámicamente. El evento handleSubmit de la <Form> solo se dispara cuando el formulario es válido.

🔥 Importante: Los campos se validan en el momento que pierden el foco (on blur) o cuando se intenta enviar el formulario. Puedes configurar este comportamiento si lo deseas.
INICIO Interacción con Formulario (Ingreso de datos) VeeValidate + Yup ¿Válido? Submit Form (Datos OK) FIN NO Mostrar errores Usuario corrige

💡 Estilizando los Mensajes de Error y Campos Inválidos

Para mejorar la experiencia de usuario, es fundamental que los campos inválidos y sus mensajes de error sean visualmente distintivos. VeeValidate no incluye estilos por defecto, pero nos facilita las clases y propiedades para aplicarlos.

1. Añadir Estilos CSS

Ya hemos añadido una clase .error-message en el CSS. También podemos aplicar estilos al input cuando es inválido. VeeValidate puede añadir clases CSS a los campos automáticamente.

Modifica el Field de la siguiente manera para que añada una clase is-invalid si el campo tiene errores:

      <Field name="username" type="text" id="username" :class="{'is-invalid': errors.username}" />
      <ErrorMessage name="username" class="error-message" />

Esto requiere acceder a los errores del formulario. Para ello, necesitamos la composición useForm o las propiedades de slot del componente Form.

2. Usando la Composición useForm (Recomendado con script setup)

Con script setup, useForm es la forma más directa de acceder a las propiedades de validación. Reemplaza tu script setup existente:

<script setup>
import { Form, Field, ErrorMessage, useForm } from 'vee-validate';
import * as yup from 'yup';
import { toTypedSchema } from '@vee-validate/yup';

const schema = toTypedSchema(
  yup.object({
    username: yup.string()
      .required('El nombre de usuario es obligatorio')
      .min(3, 'El nombre de usuario debe tener al menos 3 caracteres')
      .max(20, 'El nombre de usuario no puede exceder los 20 caracteres'),
    email: yup.string()
      .required('El correo electrónico es obligatorio')
      .email('Debe ser un correo electrónico válido'),
    password: yup.string()
      .required('La contraseña es obligatoria')
      .min(6, 'La contraseña debe tener al menos 6 caracteres'),
    confirmPassword: yup.string()
      .required('Confirma tu contraseña')
      .oneOf([yup.ref('password')], 'Las contraseñas no coinciden'),
  })
);

// Usamos useForm para obtener las funciones y el estado de validación
const { handleSubmit, errors, defineInputBinds } = useForm({
  validationSchema: schema,
});

// Podemos usar defineInputBinds para vincular los campos directamente
// const username = defineInputBinds('username');
// <input v-bind="username" :class="{'is-invalid': errors.username}" />
// Sin embargo, usar <Field> es a menudo más conveniente para la mayoría de los casos.

// La función handleSubmit ya está enlazada al useForm
const onSubmit = handleSubmit(values => {
  console.log('Formulario enviado y validado:', values);
  alert('Registro exitoso! \n' + JSON.stringify(values, null, 2));
  // Lógica para enviar el formulario al servidor
});
</script>

Con errors disponible, puedes aplicar condicionalmente la clase is-invalid a tus campos Field (o a los elementos input si no usas Field directamente):

      <Field name="username" type="text" id="username" :class="{'is-invalid': errors.username}" />
      <ErrorMessage name="username" class="error-message" />

¡Espera! El Field de VeeValidate ya inyecta estas propiedades. No necesitas pasar errors manualmente al Field si usas el slot-scope o la forma v-bind="field" directamente. Sin embargo, para fines de ejemplo, si quisieras usar un input HTML nativo en lugar de Field, errors sería útil.

Para el componente Field, VeeValidate ya añade automáticamente las clases si configuras los props. La forma más sencilla de añadir la clase is-invalid es a través del slot field de <Field>.

<template>
  <Form @submit="onSubmit" :validation-schema="schema">
    <!-- ... -->

    <div class="form-group">
      <label for="username">Nombre de Usuario:</label>
      <Field name="username" v-slot="{ field, meta }">
        <input type="text" id="username" v-bind="field" :class="{'is-invalid': meta.touched && !meta.valid}">
      </Field>
      <ErrorMessage name="username" class="error-message" />
    </div>

    <!-- Repite para otros campos -->
    <div class="form-group">
      <label for="email">Correo Electrónico:</label>
      <Field name="email" v-slot="{ field, meta }">
        <input type="email" id="email" v-bind="field" :class="{'is-invalid': meta.touched && !meta.valid}">
      </Field>
      <ErrorMessage name="email" class="error-message" />
    </div>

    <div class="form-group">
      <label for="password">Contraseña:</label>
      <Field name="password" v-slot="{ field, meta }">
        <input type="password" id="password" v-bind="field" :class="{'is-invalid': meta.touched && !meta.valid}">
      </Field>
      <ErrorMessage name="password" class="error-message" />
    </div>

    <div class="form-group">
      <label for="confirmPassword">Confirmar Contraseña:</label>
      <Field name="confirmPassword" v-slot="{ field, meta }">
        <input type="password" id="confirmPassword" v-bind="field" :class="{'is-invalid': meta.touched && !meta.valid}">
      </Field>
      <ErrorMessage name="confirmPassword" class="error-message" />
    </div>

    <button type="submit">Registrarse</button>
  </Form>
</template>

Aquí, field contiene las propiedades para vincular el input (name, id, value, onInput, onChange, onBlur). meta contiene información sobre el estado de validación del campo (touched, dirty, valid, invalid).

📌 Nota: Usar `meta.touched && !meta.valid` es una buena práctica para mostrar errores solo después de que el usuario haya interactuado con el campo y este sea inválido, evitando mostrar errores al cargar el formulario.

⚙️ Reglas de Validación Personalizadas con Yup

Yup es extremadamente extensible. Puedes añadir tus propias reglas de validación para satisfacer requisitos específicos que no están cubiertos por las reglas predefinidas.

Imaginemos que queremos una regla para asegurarnos de que el nombre de usuario no contenga caracteres especiales, solo letras y números.

1. Extender Yup

Puedes extender Yup en un archivo separado para mantener tu código limpio. Crea un archivo src/utils/yup-extensions.js:

// src/utils/yup-extensions.js
import * as yup from 'yup';

yup.addMethod(yup.string, 'noSpecialChars', function (message) {
  return this.test('no-special-chars', message, function (value) {
    const { path, createError } = this;
    return (
      (value === null || value === undefined) || /^[a-zA-Z0-9]+$/.test(value)
    ) || createError({ path, message });
  });
});

// También puedes añadir otras extensiones aquí
// yup.addMethod(yup.string, 'strongPassword', function (message) { /* ... */ });

// Importante: exportar Yup una vez extendido si lo necesitas en otros lugares
export default yup;

2. Usar la Regla Personalizada en el Esquema

Ahora, importa la versión extendida de Yup en tu RegistrationForm.vue y usa la nueva regla noSpecialChars:

<script setup>
import { Form, Field, ErrorMessage, useForm } from 'vee-validate';
// Importamos Yup desde nuestro archivo de extensiones
import * as yup from '@/utils/yup-extensions'; // Asegúrate de que la ruta sea correcta
import { toTypedSchema } from '@vee-validate/yup';

const schema = toTypedSchema(
  yup.object({
    username: yup.string()
      .required('El nombre de usuario es obligatorio')
      .min(3, 'El nombre de usuario debe tener al menos 3 caracteres')
      .max(20, 'El nombre de usuario no puede exceder los 20 caracteres')
      .noSpecialChars('El nombre de usuario solo puede contener letras y números'), // ¡Nuestra nueva regla!
    email: yup.string()
      .required('El correo electrónico es obligatorio')
      .email('Debe ser un correo electrónico válido'),
    password: yup.string()
      .required('La contraseña es obligatoria')
      .min(6, 'La contraseña debe tener al menos 6 caracteres'),
    confirmPassword: yup.string()
      .required('Confirma tu contraseña')
      .oneOf([yup.ref('password')], 'Las contraseñas no coinciden'),
  })
);

const { handleSubmit, errors } = useForm({
  validationSchema: schema,
});

const onSubmit = handleSubmit(values => {
  console.log('Formulario enviado y validado:', values);
  alert('Registro exitoso! \n' + JSON.stringify(values, null, 2));
});
</script>

Ahora, si intentas usar caracteres especiales en el nombre de usuario, verás el mensaje de error personalizado.

✍️ Explicación de `yup.addMethod`

La función `yup.addMethod` te permite extender los tipos de esquema existentes (como `yup.string`).

  • El primer argumento es el tipo al que quieres añadir el método (`yup.string`).
  • El segundo es el nombre de tu nuevo método (`'noSpecialChars'`).
  • El tercero es la función que implementa la lógica de validación. Esta función recibe el mensaje de error como argumento.
  • Dentro de la función, `this` se refiere al contexto de Yup, lo que te permite acceder a propiedades como `path` (el nombre del campo actual) y `createError` (una función para generar un error).
  • El método `test` es la base para las validaciones personalizadas. Recibe un nombre para el test, el mensaje de error y una función que devuelve `true` si es válido, o `false` y llama a `createError` si es inválido.

🔄 Resetear y Recargar Formularios

Después de enviar un formulario exitosamente, es común querer resetearlo a su estado inicial. VeeValidate facilita esto.

1. Usando la Función resetForm

La función resetForm se obtiene del hook useForm. Puedes llamarla después de un envío exitoso.

<script setup>
import { Form, Field, ErrorMessage, useForm } from 'vee-validate';
import * as yup from '@/utils/yup-extensions';
import { toTypedSchema } from '@vee-validate/yup';

const schema = toTypedSchema(
  yup.object({
    username: yup.string()
      .required('El nombre de usuario es obligatorio')
      .min(3, 'El nombre de usuario debe tener al menos 3 caracteres')
      .max(20, 'El nombre de usuario no puede exceder los 20 caracteres')
      .noSpecialChars('El nombre de usuario solo puede contener letras y números'),
    email: yup.string()
      .required('El correo electrónico es obligatorio')
      .email('Debe ser un correo electrónico válido'),
    password: yup.string()
      .required('La contraseña es obligatoria')
      .min(6, 'La contraseña debe tener al menos 6 caracteres'),
    confirmPassword: yup.string()
      .required('Confirma tu contraseña')
      .oneOf([yup.ref('password')], 'Las contraseñas no coinciden'),
  })
);

const { handleSubmit, errors, resetForm } = useForm({
  validationSchema: schema,
});

const onSubmit = handleSubmit(async (values) => {
  console.log('Formulario enviado y validado:', values);
  alert('Registro exitoso! \n' + JSON.stringify(values, null, 2));

  // Simular un envío asíncrono
  await new Promise(resolve => setTimeout(resolve, 1000));

  // Resetear el formulario después del envío exitoso
  resetForm();

  console.log('Formulario reseteado.');
});
</script>

La función resetForm() sin argumentos reseteará todos los campos a sus valores iniciales (o a vacío si no se especificaron valores iniciales). Puedes pasar un objeto para resetear a valores específicos o cambiar el estado dirty o touched.

💡 Consejo: Si necesitas resetear el formulario a valores predeterminados específicos, puedes pasar un objeto a `resetForm({ values: { username: 'default', email: '' } })`.

2. Establecer Valores Iniciales

Si quieres que tu formulario comience con valores, puedes pasarlos a useForm.

const { handleSubmit, errors, resetForm } = useForm({
  validationSchema: schema,
  initialValues: {
    username: 'john.doe',
    email: 'john.doe@example.com',
    password: '',
    confirmPassword: ''
  }
});

Ahora, cuando el componente se monte, el formulario se inicializará con estos valores. Al llamar a resetForm(), volverá a estos valores iniciales.


🔍 Explorando la Composition API: useField y useForm Directo

Para escenarios más avanzados o para mayor flexibilidad, VeeValidate ofrece useField y useForm como funciones composables. Esto te permite tener un control más granular sobre los campos y la validación, especialmente útil si no quieres usar los componentes <Form>, <Field> y <ErrorMessage> directamente.

Consideremos un ejemplo donde creamos un campo personalizado o integramos con un componente de UI de terceros.

<template>
  <form @submit.prevent="onSubmit" class="custom-form">
    <h3>Formulario con useForm y useField</h3>

    <div class="form-group">
      <label for="name">Nombre Completo:</label>
      <input type="text" id="name" v-model="name.value" :class="{'is-invalid': name.meta.touched && !name.meta.valid}">
      <span v-if="name.meta.touched && !name.meta.valid" class="error-message">{{ name.errorMessage }}</span>
    </div>

    <div class="form-group">
      <label for="age">Edad:</label>
      <input type="number" id="age" v-model.number="age.value" :class="{'is-invalid': age.meta.touched && !age.meta.valid}">
      <span v-if="age.meta.touched && !age.meta.valid" class="error-message">{{ age.errorMessage }}</span>
    </div>

    <button type="submit" :disabled="!meta.valid">Enviar Registro</button>
    <button type="button" @click="resetForm">Limpiar</button>
  </form>
</template>

<script setup>
import { useForm, useField } from 'vee-validate';
import * as yup from 'yup';
import { toTypedSchema } from '@vee-validate/yup';

const customSchema = toTypedSchema(
  yup.object({
    name: yup.string()
      .required('El nombre es obligatorio')
      .min(2, 'El nombre debe tener al menos 2 caracteres'),
    age: yup.number()
      .required('La edad es obligatoria')
      .positive('La edad debe ser un número positivo')
      .integer('La edad debe ser un número entero')
      .min(18, 'Debes ser mayor de 18 años'),
  })
);

const { handleSubmit, resetForm, meta } = useForm({
  validationSchema: customSchema,
});

// Registramos cada campo usando useField
const name = useField('name');
const age = useField('age');

// La función de envío del formulario
const onSubmit = handleSubmit(values => {
  console.log('Formulario personalizado enviado:', values);
  alert('Formulario personalizado enviado! \n' + JSON.stringify(values, null, 2));
});
</script>

<style scoped>
/* Estilos para el formulario personalizado, similar al anterior */
.custom-form {
  max-width: 400px;
  margin: 30px auto;
  padding: 25px;
  border: 1px solid #d0d0d0;
  border-radius: 6px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  background-color: #fcfcfc;
}
.custom-form h3 {
  text-align: center;
  color: #444;
  margin-bottom: 25px;
}
.custom-form button {
  margin-right: 10px;
}
</style>

Explicación:

  • useForm: Se llama para inicializar el contexto del formulario. Nos devuelve handleSubmit, resetForm y meta (que contiene el estado general de validez del formulario).
  • useField('name'): Se llama para cada campo individual. Acepta el nombre del campo como argumento. Retorna un objeto reactivo con:
    • value: El valor actual del campo, que se enlaza con v-model.
    • errorMessage: El mensaje de error actual para ese campo.
    • meta: Objeto con el estado del campo (touched, dirty, valid, invalid).
  • Enlazando manualmente: En lugar de <Field> y <ErrorMessage>, ahora vinculamos v-model a field.value, mostramos field.errorMessage manualmente y aplicamos clases condicionalmente usando field.meta.

Esta aproximación ofrece la máxima flexibilidad, ideal para componentes de UI personalizados o cuando necesitas un control muy específico sobre cómo se renderizan los campos y los errores.

¡Casi listo!

🌍 Internacionalización de Mensajes de Error

Los mensajes de error predeterminados de Yup y VeeValidate están en inglés. Para aplicaciones multilingües, querrás traducirlos. VeeValidate permite configurar un validador para cambiar los mensajes.

1. Configurar un Archivo de Localización

Crea un archivo src/locales/es.js para los mensajes en español:

// src/locales/es.js
import { setLocale } from 'yup';

setLocale({
  mixed: {
    required: 'Este campo es obligatorio',
    oneOf: 'El valor debe ser uno de los siguientes: ${values}',
    notOneOf: 'El valor no debe ser uno de los siguientes: ${values}',
    notType: ({ path, type, value, originalValue }) => {
      // Personaliza aquí si el tipo no coincide
      switch (type) {
        case 'number':
          return `${path} debe ser un número`;
        case 'boolean':
          return `${path} debe ser un booleano`;
        case 'string':
          return `${path} debe ser un texto`;
        default:
          return `Tipo incorrecto para ${path}: se esperaba ${type}, se obtuvo ${typeof value}`;
      }
    },
  },
  string: {
    min: 'Este campo debe tener al menos ${min} caracteres',
    max: 'Este campo no puede exceder los ${max} caracteres',
    email: 'Debe ser un correo electrónico válido',
    url: 'Debe ser una URL válida',
    trim: 'Este campo no debe tener espacios al principio o al final',
    lowercase: 'Este campo debe estar en minúsculas',
    uppercase: 'Este campo debe estar en mayúsculas',
  },
  number: {
    min: 'Este campo debe ser mayor o igual a ${min}',
    max: 'Este campo debe ser menor o igual a ${max}',
    lessThan: 'Este campo debe ser menor que ${less}',
    moreThan: 'Este campo debe ser mayor que ${more}',
    positive: 'Este campo debe ser un número positivo',
    negative: 'Este campo debe ser un número negativo',
    integer: 'Este campo debe ser un número entero',
  },
  array: {
    min: 'Este campo debe tener al menos ${min} elementos',
    max: 'Este campo no puede exceder los ${max} elementos',
  },
  date: {
    min: 'La fecha debe ser posterior a ${min}',
    max: 'La fecha debe ser anterior a ${max}',
  }
});

2. Importar en main.js o donde sea Global

Para que estos mensajes se apliquen globalmente, importa el archivo de localización en tu main.js:

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import './assets/main.css'; // O tu archivo CSS principal

// Importa tu archivo de localización para Yup
import './locales/es'; 

createApp(App).mount('#app');

Ahora, los mensajes de error de Yup (y por lo tanto, los de VeeValidate cuando usas validation-schema) deberían aparecer en español.


✅ Conclusión y Próximos Pasos

Has aprendido a implementar un sistema de validación de formularios reactivo y robusto en Vue 3 utilizando VeeValidate y Yup. Hemos cubierto desde la configuración básica hasta la creación de reglas personalizadas y la internacionalización de mensajes de error.

La combinación de VeeValidate y Yup ofrece una solución extremadamente potente y flexible para manejar las complejidades de la validación de formularios, permitiéndote construir aplicaciones más fiables y con una mejor experiencia de usuario.

Puntos Clave Cubiertos:

  • 🚀 Configuración de un proyecto Vue 3 con VeeValidate y Yup.
  • 📝 Creación de formularios con los componentes <Form>, <Field> y <ErrorMessage>.
  • 💡 Definición de esquemas de validación complejos con Yup.
  • ⚙️ Creación y uso de reglas de validación personalizadas con yup.addMethod.
  • 🔄 Gestión del estado del formulario: resetForm y initialValues.
  • 🔍 Uso de la Composition API (useForm, useField) para un control más granular.
  • 🌍 Internacionalización de mensajes de error de Yup.
Paso 1: Entender la necesidad de validación.
Paso 2: Instalar VeeValidate y Yup.
Paso 3: Construir el formulario con `
`, ``, ``.
Paso 4: Definir el esquema con Yup.
Paso 5: Añadir reglas personalizadas.
Paso 6: Considerar `useForm` y `useField` para casos avanzados.
Paso 7: Localizar mensajes de error.

¿Qué Sigue?

  • Validación Asíncrona: Explora cómo realizar validaciones contra un servidor (por ejemplo, comprobar si un nombre de usuario ya existe) con VeeValidate. Puedes integrar funciones asíncronas en tus validaciones de Yup.
  • Validación Condicional: Aprende a aplicar reglas de validación solo bajo ciertas condiciones (por ejemplo, un campo es requerido solo si otro tiene un valor específico).
  • Componentes de Entrada Personalizados: Crea tus propios componentes de entrada (por ejemplo, un selector de fechas personalizado) y aprende a integrarlos con VeeValidate usando useField.
  • Integración con UI Frameworks: Si usas un framework de UI como Vuetify, PrimeVue o Element Plus, explora las integraciones específicas de VeeValidate para estos.

¡Felicidades! Ahora tienes las herramientas para crear formularios complejos y bien validados en tus aplicaciones Vue 3. ¡Sigue experimentando y construyendo!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!