Livewire Confirm: Diálogos de Confirmación, Prompt y Modales en Laravel (Guía Completa)

Laravel Livewire continúa evolucionando con pequeñas pero poderosas mejoras que simplifican tareas comunes en aplicaciones modernas. Una de estas funcionalidades es wire:confirm, una forma rápida y elegante de agregar diálogos de confirmación directamente desde tus componentes, sin necesidad de escribir JavaScript adicional.

En este artículo exploraremos cómo implementar confirmaciones en Livewire utilizando diferentes enfoques: desde la simplicidad de wire:confirm en una sola línea, pasando por confirmaciones avanzadas con prompt que requieren validación del usuario, hasta el uso de modales personalizados con Flux para una experiencia más profesional. También veremos cómo usar confirmaciones tradicionales con JavaScript cuando necesitamos mayor control.

Si trabajas con acciones críticas como eliminar registros, dominar estas técnicas no solo mejora la experiencia de usuario, sino que también previene errores comunes y hace que tus aplicaciones sean mucho más robustas y seguras.

Diálogo de Confirmación en UNA SOLA LíNEA en Laravel Livewire: Wire:Confirm

Video thumbnail

Aquí te quiero presentar una pequeña maravilla que realmente yo siempre estoy leyendo la documentación de los cursos que voy como quien dice haciendo sobre la marcha para como quien dice siempre tener lo último de lo último y viene precisamente siendo esto el de:

wire:confirm

Ya creo que esto nos dice bastante y ya pudiéramos inclusive dejarlo hasta acá porque obviamente o sea es bastante obvio pero de igual manera vamos a hacer una pequeña demostración. Simplemente vamos a modificar el botón que por defecto, llama a un método llamado delete, para que ahora, definiendo el parámetro anterior, aparezca un dialogo de confirmación y al presionarlo, se ejecuta el script definido en wire:click:

<flux:button class="ml-3" variant='danger' size="xs" wire:click="delete({{ $c }})" wire:confirm="Are you sure you want to delete this category?">
     {{ __('Delete') }}
</flux:button>

Confirmación con “Prompt”

Otra variante muy interesante es indicar un prompt. Según la documentación oficial, esto obliga al usuario a escribir una palabra específica antes de realizar la acción.

Tal como ocurre en GitHub cuando quieres eliminar un repositorio y te pide escribir su nombre.

Para implementarlo, seguimos este esquema:

  1. Indicamos el mensaje principal.
  2. Añadimos el modificador .prompt.
  3. Pasamos un segundo mensaje indicando qué debe escribir el usuario (por ejemplo: "Escribe DELETE para continuar").
<flux:button size="sm" variant="danger" wire:confirm.prompt="{{ __('Are you sure?') }}\n\nType DELETE to confirm|DELETE" wire:click='delete({{ $category }})'>{{ __('Delete') }}</flux:button>

Puedes usar interpolación de texto para que la palabra clave sea dinámica (como el slug del post), pero para este ejercicio lo mantendremos fijo.

Pruebas del Prompt:

  • Si le das a eliminar y escribes cualquier cosa, no hace nada.
  • Si no configuras correctamente la palabra clave, Livewire te mostrará un warning indicando que la configuración no es válida.
  • Solo cuando escribes exactamente la palabra definida (en este caso, "DELETE"), el sistema intenta ejecutar el método de eliminación.

Componente Modal de Flux

Flux también tiene un componente de modal, veamos una implementación al momento de eliminar:

resources/views/livewire/dashboard/category/save.blade.php

resources\views\pages\dashboard\category\⚡save.blade.php

<flux:modal name="delete-category">
   <div class="m-1">
       <flux:heading>{{ __('Delete Category') }}</flux:heading>
       <flux:text class="mt-2">{{ __('Are you sure you want to delete this category?') }}</flux:text>

       <div class="flex flex-row-reverse">
       <flux:button class="mt-4" variant='danger' icon="trash">
           {{ __('Delete') }}
       </flux:button>
       </div>
   </div>
</flux:modal>

El nombre, debe ser único, ya que es un identificador que emplearemos para accionar el modal; para accionarlo, tenemos varias formas, entre las principales, mediante un componente trigger, que es la que implementaremos:

resources/views/livewire/dashboard/category/save.blade.php

resources\views\pages\dashboard\category\⚡save.blade.php

<a href="{{ route('d-category-edit', $c) }}">Edit</a>

<flux:modal.trigger name="delete-category">
   <flux:button class="ml-3" variant='danger' size="xs">{{ __('Delete') }}</flux:button>
</flux:modal.trigger>

Fíjate que empleamos el nombre definido para el modal; para el diseño de botón, puedes personalizarlo a gusto.

También podemos activarlo desde la clase componente:

// Control "confirm" modals anywhere on the page...
Flux::modal('confirm')->show();
Flux::modal('confirm')->close();

// Control "confirm" modals within this Livewire component...
$this->modal('confirm')->show();
$this->modal('confirm')->close();

// Closes all modals on the page...
Flux::modals()->close();

Mediante JavaScript:

<button x-on:click="$flux.modal('confirm').show()">
   Open modal
</button>

<button x-on:click="$flux.modal('confirm').close()">
   Close modal
</button>

<button x-on:click="$flux.modals().close()">
   Close all modals
</button>

***

// Control "confirm" modals anywhere on the page...
Flux.modal('confirm').show()
Flux.modal('confirm').close()

// Closes all modals on the page...
Flux.modals().close()

O el objeto wire que conoceremos más adelante:

<flux:button x-on:click="$wire.showConfirmModal = true">Delete post</flux:button>

Vamos a realizar la siguiente adaptación:

resources/views/livewire/dashboard/category/index.blade.php

resources\views\pages\dashboard\category\⚡index.blade.php

***
<flux:modal name="delete-category">
   ***
       <flux:button class="mt-4" variant='danger' icon="trash" wire:click="delete()">
           {{ __('Delete') }}
       </flux:button>
       </div>
   </div>
</flux:modal>

Explicación del código anterior

La opción de eliminar una categoría la cual fue migrada al apartado de acciones, para cuando presionemos un botón de eliminar; aunque tiene un cambio importante, ya que, ahora no estamos pasando por referencia la categoría que queremos eliminar, esto lo establecemos desde antes, desde el apartarlo de los enlaces de acciones en la tabla de categorías:

<flux:modal.trigger wire:click="selectCategodyToDelete({{ $c }})" name="delete-category">
   <flux:button class="ml-3" variant='danger' size="xs">{{ __('Delete') }}</flux:button>
</flux:modal.trigger>

Este proceso intermediario se debe a que, no podemos pasar la referencia de la categoría directamente, ya que, en el listado tenemos N cantidad de categorías y solamente un modal; por lo tanto, cuando presionamos sobre eliminar una categoría, lo que hacemos es guardar la referencia a la categoría que queremos eliminar en una propiedad en la clase componente:

public function selectCategodyToDelete(Category $category) {
 $this->categoryToDelete = $category;
}

La definición del método selectCategodyToDelete() se encuentra definida en la clase componente:

app/Http/Livewire/Dashboard/Category/Index.php

resources\views\pages\dashboard\category\⚡index.blade.php

class Index extends Component
{

   use WithPagination;

   public $categoryToDelete;

   public function seletedCategoryToDelete(Category $category)
   {
       $this->categoryToDelete = $category;
   }

   public function delete()
   {
       $this->dispatch("deleted");
       $this->categoryToDelete->delete();
   }
}

Explicación del código anterior

Como mencionamos antes, con el método intermediaria seleccionamos la categoría y la colocamos en una propiedad, que luego es usada cuando se activa el evento click desde el botón de eliminar del modal.

Finalmente, para ocultar el modal:

function delete(){
   $this->dispatch("deleted");
   Flux::modal("delete-category")->close();
   $this->categoryToDelete->delete();
}

Con confirm() de JavaScript

Un diálogo de confirmación no es más que una caja de diálogo que se muestra en el navegador para confirmar una acción que se realizará en una aplicación web. Por ejemplo, si un usuario hace clic en un botón que borra un registro en una tabla, se puede mostrar un cuadro de diálogo de confirmación que pregunte al usuario si está seguro de que desea borrar el registro. veamos como implementar uno en Livewire.

Laravel Livewire es genial, realizar operaciones que requieren de ambos lados, es decir, el cliente y el servidor siempre son pesadas ya que tenemos que crear rest apis, y conectar ambos entes, o desde vanilla JavaScript, enviar peticiones fetch, devolver json, evaluar respuesta, hacer cambios en el html... un verdadero lío y mucho trabajo para hacer actualizaciones; Livewire nos abstrae de todo esto empleando eventos y funciones, además de una estructura base para poder comunicar el cliente y servidor de una manera muy directa.

El problema radica que esta facilidad a veces nos trae inflexibilidad, y realizar operaciones sencillas como eliminar, quedan demasiadas sencillas y directas con livewire; generalmente tenemos en Livewire algo como:

<flux:button wire:click="delete({{ $p }})" href="#">
      Eliminar

Que en la práctica si le pegamos un clic al elemento HTML que tiene definido dicho atributo ELIMINA DE UNO EL ELEMENTO, cosa que seguramente no vas a querer porque puede que nuestro usuario le dé un clic por error a esa opción; para las eliminaciones es buena práctica colocar un dialogo de confirmacion; asi que, para evitar eliminar registros con un solo clic, en el mismo elemento que contiene el wire:click, puedes colocarle un onclick de JavaScript con dicho diálogo de confirmación:

<flux:button onclick="confirm('¿Seguro que quieres eliminar?') || event.stopImmediatePropagation()"  wire:click="delete({{ $p }})" href="#">
      Eliminar
</flux:button>

La clave aquí es el stopImmediatePropagation que detiene la programación del evento; livewire en su core emplea eventos en el cliente para llamar a eventos en el servidor, eventos de JavaScript que también podemos detener.

Aprende a implementar confirmaciones en Laravel Livewire usando wire:confirm, prompts, JavaScript y modales con Flux. Mejora la UX y evita eliminaciones accidentales paso a paso.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english