Chris
Chris White Web Developer

Implementing Inline Success Indicator with Livewire and AlpineJS

12 February 2022 ~1 minute read

I was recently building a feature into a web application that allows an administrator to change user roles. I wanted the role to be persisted to the database immediately after it was changed, and I struggled to think of a nice way to give the user feedback that their role change was successful. In the end I landed on something very simple. Showing a little green check mark next to the dropdown that disappears after a second or so:

The project uses Livewire and AlpineJS, and building this feedback isn't immediately intuitive, so here's how I ended up doing it.

This is our markup for the select input.

1<select name="role"
2 wire:model="role"
3 wire:change="updateRole($event.target.value)"
4>
5 <option value="admin">Admin</option>
6 <option value="user">User</option>
7</select>

And this is the updateRole() method that exists in the Livewire component. This is what persists the user's new role to the database after it has been changed, using wire:change from above. I've removed the cruft and highlighted the part that matters: emitting the updated event.

1public function updateRole(string $role): void
2{
3 // Save $role to your database somehow.
4 
5 $this->emit('updated');
6}

So now we have a dropdown that lets the administrator change roles that then get persisted to the database. But how do we show the feedback? Enter AlpineJS, right after the select element:

1<select name="role"
2 wire:model="role"
3 wire:change="updateRole($event.target.value)"
4>
5 <option value="admin">Admin</option>
6 <option value="user">User</option>
7</select>
8 
9<div x-data="{ show: false, timeout: null }"
10 x-show="show"
11 x-init="@this.on('updated', () => { clearTimeout(timeout); show = true; timeout = setTimeout(() => { show = false }, 1000); })"
12 x-transition.duration.500ms>
13 <X-heroicon-s-check-circle class="inline h-5 ml-4 text-green-500" />
14</div>

There's a bit to unpack here. First, on line 9, we're initializing an AlpineJS component with two properties:

  • show - This boolean dictates if we're showing the feedback or not
  • timeout - This holds the timer created by setTimeout() so that we may clear it later

Then on line 10 we're only showing the feedback if show is true (remember it is false by default).

Line 11 is the real meat and potatoes. We're listening for that updated event emitted by our Livewire component when a role is successfully changed. We run a function in response to that event which cancels any timeouts that we had set previously (from previous role changes). It also sets show to true, showing the success feedback. We finally set the timeout property to the result of setTimeout(). The function we pass into setTimeout() sets show back to false after 1 second.

Finally, on line 12, we just add a transition to make things look a little smoother.

Made with Jigsaw and Torchlight.