Publicada:

Vue 3 + VeeValidate - Mostrar error personalizado para solicitud de API HTTP fallida

Tutorial creado con Vue 3.2.33 y VeeValidate 4.5.11

Este es un ejemplo rápido de cómo mostrar un mensaje de error personalizado en Vue 3 con VeeValidate después de una solicitud HTTP (AJAX) fallida a una API.

El siguiente componente es parte de un tutorial de autenticación de Vue 3 JWT que publiqué recientemente que incluye una demostración en vivo, por lo que para ver el código en ejecución, consulte Vue 3 + Pinia - Tutorial y ejemplo de autenticación JWT.

Cuando se envían credenciales no válidas en el formulario, la API devuelve el mensaje de error 'Username or password is incorrect' y se muestra debajo del botón de inicio de sesión.

 

Componente LoginView de Vue 3

Ruta: /src/views/LoginView.vue

La vista de inicio de sesión contiene un formulario creado con la biblioteca VeeValidate que contiene campos de nombre de usuario y contraseña para iniciar sesión en la aplicación Vue 3.

Las reglas de validación de formularios se definen con la biblioteca de validación de esquemas de Yup que VeeValidate admite de fábrica. Para obtener más información sobre Yup, consulte https://github.com/jquense/yup.

Establecer error personalizado en el método onSubmit() del formulario

El método onSubmit() publica las credenciales de usuario en la API llamando a authStore.login(). Si la solicitud de inicio de sesión falla, se establece un error personalizado apiError en el formulario usando la función setErrors() de VeeValidate, y el error de API devuelto se representa en la plantilla de Vue a través de VeeValidate objeto de errores - {{errors.apiError}}.

Cuando se inicia sesión correctamente, los datos de usuario devueltos se almacenan en el estado global de Pinia mediante el método de acción login() en auth store, el método luego redirige al usuario a la página de inicio.

La plantilla del componente Vue contiene el formulario con campos de entrada y mensajes de validación. El formulario y los campos se construyen con los componentes VeeValidate <Form /> y <Field /> que se conectan automáticamente a las reglas de validación (esquema) según el nombre del campo.

El formulario llama al método onSubmit() cuando el formulario se envía y es válido. Las reglas de validación están vinculadas al formulario con la propiedad validation-schema, y los errores de validación se proporcionan a la plantilla de formulario a través de la ranura de ámbito v-slot="{ errors }". Para obtener más información sobre la validación de formularios con Vue 3 y VeeValidate, consulte Vue 3 + VeeValidate - Ejemplo de validación de formulario (API de composición).

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

import { useAuthStore } from '@/stores';

const schema = Yup.object().shape({
    username: Yup.string().required('Username is required'),
    password: Yup.string().required('Password is required')
});

function onSubmit(values, { setErrors }) {
    const authStore = useAuthStore();
    const { username, password } = values;

    return authStore.login(username, password)
        .catch(error => setErrors({ apiError: error }));
}
</script>

<template>
    <div>
        <div class="alert alert-info">
            Username: test<br />
            Password: test
        </div>
        <h2>Login</h2>
        <Form @submit="onSubmit" :validation-schema="schema" v-slot="{ errors, isSubmitting }">
            <div class="form-group">
                <label>Username</label>
                <Field name="username" type="text" class="form-control" :class="{ 'is-invalid': errors.username }" />
                <div class="invalid-feedback">{{errors.username}}</div>
            </div>            
            <div class="form-group">
                <label>Password</label>
                <Field name="password" type="password" class="form-control" :class="{ 'is-invalid': errors.password }" />
                <div class="invalid-feedback">{{errors.password}}</div>
            </div>            
            <div class="form-group">
                <button class="btn btn-primary" :disabled="isSubmitting">
                    <span v-show="isSubmitting" class="spinner-border spinner-border-sm mr-1"></span>
                    Login
                </button>
            </div>
            <div v-if="errors.apiError" class="alert alert-danger mt-3 mb-0">{{errors.apiError}}</div>
        </Form>
    </div>
</template>
 

Almacén de autenticación de Pinia

Ruta: /src/stores/auth.store.js

El almacén de autenticación contiene el estado de Pinia y acciones para la autenticación. La propiedad de estado user contiene el usuario conectado actual, se inicializa con el objeto 'user' del almacenamiento local para permitir permanecer conectado entre actualizaciones de página y sesiones del navegador, o null si localStorage está vacío.

El método de acción login() de Pinia publica las credenciales en la API; si tiene éxito, el objeto de usuario devuelto se almacena en el estado de Pinia y localStorage, y el enrutador redirige a la URL de retorno o a la página de inicio. Si falla, el método asíncrono arroja un error que se detecta y se muestra dentro del componente LoginView.

El método de acción logout() establece al usuario como nulo en el estado de Pinia, lo elimina del almacenamiento local y lo redirige a la página de inicio de sesión.

import { defineStore } from 'pinia';

import { fetchWrapper, router } from '@/helpers';

const baseUrl = `${import.meta.env.VITE_API_URL}/users`;

export const useAuthStore = defineStore({
    id: 'auth',
    state: () => ({
        // initialize state from local storage to enable user to stay logged in
        user: JSON.parse(localStorage.getItem('user')),
        returnUrl: null
    }),
    actions: {
        async login(username, password) {
            const user = await fetchWrapper.post(`${baseUrl}/authenticate`, { username, password });

            // update pinia state
            this.user = user;

            // store user details and jwt in local storage to keep user logged in between page refreshes
            localStorage.setItem('user', JSON.stringify(user));

            // redirect to previous url or default to home page
            router.push(this.returnUrl || '/');
        },
        logout() {
            this.user = null;
            localStorage.removeItem('user');
            router.push('/login');
        }
    }
});
 


Suscríbete o Sígueme para actualizaciones

Suscríbete a mi canal de YouTube o sígueme en Twitter, Facebook o GitHub para recibir notificaciones cuando publique contenido nuevo.

Aparte de la codificación...

Actualmente estoy intentando viajar por Australia en motocicleta con mi esposa Tina en un par de Royal Enfield Himalayan. Puedes seguir nuestras aventuras en YouTube, Instagram y Facebook.


¿Necesita Ayuda Vue 3?

Buscar fiverr para encontrar ayuda rápidamente de desarrolladores Vue 3 experimentados.