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

    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.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *