Cómo crear un diálogo de confirmación en Vue.js

C

Introducción

Un diálogo de confirmación es un patrón de interfaz de usuario en el que se le dará al usuario la opción de continuar con su acción o cancelarla. Se usa comúnmente con acciones destructivas o irreversibles, para asegurarse de que el usuario realmente quiera continuar.

En este artículo, implementaremos un diálogo de confirmación modular y reutilizable en Vue.js.

Creación de un componente emergente reutilizable

Comencemos por crear un componente base reutilizable para cualquier tipo de componente emergente. De esa manera, no tenemos que volver a implementar la mecánica emergente varias veces. Esto se puede reutilizar más tarde para crear cualquier cosa, desde un cuadro de alerta hasta una ventana emergente de boletín.

Comencemos con la plantilla:

<!-- components/PopupModal.vue -->

<template>
    <transition name="fade">
        <div class="popup-modal" v-if="isVisible">
            <div class="window">
                <slot></slot>
            </div>
        </div>
    </transition>
</template>

Observe que agregamos un vacío <slot></slot> etiqueta a la plantilla. Esta etiqueta nos permite insertar cualquier contenido en el PopupModal elemento en el <slot></slot> etiqueta. Para leer más sobre cómo funcionan las tragamonedas, consulte la Guía de Vue en las tragamonedas.

También hemos agregado el <transition name="fade"> etiqueta a la plantilla. Usaremos esto en la siguiente sección para animar un efecto de aparición / desaparición gradual en el diálogo.

Luego, agregaremos el data(), open() y close() funciones de eventos:

<!-- components/PopupModal.vue -->

<script>
export default {
    name: 'PopupModal',

    data: () => ({
        isVisible: false,
    }),

    methods: {
        open() {
            this.isVisible = true
        },

        close() {
            this.isVisible = false
        },
    },
}
</script>

Y finalmente, agreguemos un poco de estilo:

<!-- components/PopupModal.vue -->

<style scoped>
/* css class for the transition */
.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
    opacity: 0;
}

.popup-modal {
    background-color: rgba(0, 0, 0, 0.5);
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 0.5rem;
    display: flex;
    align-items: center;
    z-index: 1;
}

.window {
    background: #fff;
    border-radius: 5px;
    box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2);
    max-width: 480px;
    margin-left: auto;
    margin-right: auto;
    padding: 1rem;
}
</style>

Animación de diálogo de confirmación

En la etiqueta de la plantilla verá una etiqueta de transición <transition name="fade">. Se utiliza para animar estados simples de entrada / salida. Todo lo que esté dentro de esta etiqueta se animará si se agregó o se quitó de la etiqueta.

Estamos usando un condicional v-if="isVisible" para ocultar y mostrar la ventana emergente. Puede leer más sobre esto en el Guía de Vue sobre transiciones.

Para especificar cómo cambia el contenido, hemos llamado a nuestra animación fade. Para implementar esta transición en CSS, agregaremos clases con el prefijo del nombre fade, coincidiendo con nuestro name atributo del <transition> etiqueta.

Todo lo que hace es animar la opacidad cuando la ventana emergente se cierra y se abre:

.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
    opacity: 0;
}

Heredar el componente emergente

Para crear nuestro diálogo de confirmación, heredaremos el PopupModal por composición y personalice la ventana modal reutilizable para que se convierta en un diálogo de confirmación.

Creemos un nuevo archivo, components/ConfirmDialogue.vue y define una plantilla dentro de ella:

<!-- components/ConfirmDialogue.vue -->

<template>
    <popup-modal ref="popup">
        <h2 style="margin-top: 0">{{ title }}</h2>
        <p>{{ message }}</p>
        <div class="btns">
            <button class="cancel-btn" @click="_cancel">{{ cancelButton }}</button>
            <span class="ok-btn" @click="_confirm">{{ okButton }}</span>
        </div>
    </popup-modal>
</template>

Porque definimos el <slot></slot> etiqueta en el popup-modal componente, todo lo que ponemos entre sus etiquetas de componente (<popup-modal></popup-modal>) se representará entre sus <slot> etiquetas en su lugar.

También agregamos un ref="popup" al popup-modal etiqueta. Al establecer ese atributo, ahora podemos acceder al popup-modal instancia con this.$refs.popup. Usaremos esa referencia para llamar open() y close() en el modal emergente.

Luego, implementemos los métodos del componente padre:

<!-- components/ConfirmDialogue.vue -->

<script>
import PopupModal from './PopupModal.vue'

export default {
    name: 'ConfirmDialogue',

    components: { PopupModal },

    data: () => ({
        // Parameters that change depending on the type of dialogue
        title: undefined,
        message: undefined, // Main text content
        okButton: undefined, // Text for confirm button; leave it empty because we don't know what we're using it for
        cancelButton: 'Go Back', // text for cancel button
        
        // Private variables
        resolvePromise: undefined,
        rejectPromise: undefined,
    }),

    methods: {
        show(opts = {}) {
            this.title = opts.title
            this.message = opts.message
            this.okButton = opts.okButton
            if (opts.cancelButton) {
                this.cancelButton = opts.cancelButton
            }
            // Once we set our config, we tell the popup modal to open
            this.$refs.popup.open()
            // Return promise so the caller can get results
            return new Promise((resolve, reject) => {
                this.resolvePromise = resolve
                this.rejectPromise = reject
            })
        },

        _confirm() {
            this.$refs.popup.close()
            this.resolvePromise(true)
        },

        _cancel() {
            this.$refs.popup.close()
            this.resolvePromise(false)
            // Or you can throw an error
            // this.rejectPromise(new Error('User cancelled the dialogue'))
        },
    },
}
</script>

Finalmente, agreguemos un poco de estilo para que se vea un poco mejor:

<!-- components/ConfirmDialogue.vue -->

<style scoped>
.btns {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

.ok-btn {
    color: red;
    text-decoration: underline;
    line-height: 2.5rem;
    cursor: pointer;
}

.cancel-btn {
    padding: 0.5em 1em;
    background-color: #d5eae7;
    color: #35907f;
    border: 2px solid #0ec5a4;
    border-radius: 5px;
    font-weight: bold;
    font-size: 16px;
    text-transform: uppercase;
    cursor: pointer;
}
</style>

Uso del diálogo de confirmación

Para utilizar el diálogo de confirmación, debe incluir solo el components/ConfirmDialogue.vue componente. Por ejemplo, creemos una página con un botón ‘Eliminar’ que se asegure de si realmente desea eliminar otra página:

<template>
    <div>
        <h1>Delete Page</h1>
        <button class="delete-btn" @click="doDelete">Delete Page</button>
        <confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
    </div>
</template>

<script>
import ConfirmDialogue from '../components/ConfirmDialogue.vue'

export default {
    components: { ConfirmDialogue },
    methods: {
        async doDelete() {
            const ok = await this.$refs.confirmDialogue.show({
                title: 'Delete Page',
                message: 'Are you sure you want to delete this page? It cannot be undone.',
                okButton: 'Delete Forever',
            })
            // If you throw an error, the method will terminate here unless you surround it wil try/catch
            if (ok) {
                alert('You have successfully delete this page.')
            } else {
                alert('You chose not to delete this page. Doing nothing now.')
            }
        },
    },
}
</script>

<style scoped>
.delete-btn {
    padding: 0.5em 1em;
    background-color: #eccfc9;
    color: #c5391a;
    border: 2px solid #ea3f1b;
    border-radius: 5px;
    font-weight: bold;
    font-size: 16px;
    text-transform: uppercase;
    cursor: pointer;
}
</style>

Ya que estamos usando await en nuestro método para obtener el resultado del diálogo de confirmación, necesitamos agregar async a nuestra definición de método.

Alternativamente, puede preferir el enfoque de estilo de promesa:

this.$refs.confirmDialogue.show({
    title: 'Delete Page',
    message: 'Are you sure you want to delete this page? It cannot be undone.',
    okButton: 'Delete Forever',
}).then((result) => {
    if (ok) {
        alert('You have successfully delete this page.')
    } else {
        alert('You chose not to delete this page. Doing nothing now.')
    }
})

Para ver por qué hemos sugerido lanzar un error si el usuario cancela el diálogo de confirmación, vea qué tan fluido es el siguiente código:

await this.$refs.confirmDialogue.show({
    title: 'Delete Page',
    message: 'Are you sure you want to delete this page? It cannot be undone.',
    okButton: 'Delete Forever',
})
alert('Deleting this page.')

Dado que la cancelación no requiere ninguna acción, simplemente no hay necesidad de manejar ese estado en absoluto. Y si decide manejar una solicitud de cancelación, simplemente envuelva ese código con un try/catch.

Conclusión

En este artículo, definimos un componente emergente modal reutilizable en Vue.js y lo heredamos para implementar un diálogo de confirmación. Luego, le agregamos animaciones con fines estéticos y ejecutamos un par de ejemplos de cómo usar el componente para solicitar información a los usuarios.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad