DEV Community

Saurabh Mahajan
Saurabh Mahajan

Posted on • Updated on

Livewire Button Component with Loading Indicator

In this Article, we will see how to customize our Button to have following Animation using Livewire and extract everything into a Blade Component so as to reuse anywhere into your application.

Preview

We are going to use the Breeze Button Component and extend it according to our needs. Its definition looks like below:

<button {{ $attributes->merge(['type' => 'submit']) }}>
    {{ $slot }}
</button>
Enter fullscreen mode Exit fullscreen mode

I have removed all the CSS Classes for clarify.We can use this Button Component inside our Livewire Component like below:

<x-button wire:click="save" wire:loading.attr="disabled">
    Save
</x-button>
Enter fullscreen mode Exit fullscreen mode

What we want is that whenever this button is clicked, we want to change the Text to Saving. In order to do that we will use the wire:loading property.

<x-button wire:click="save" wire:loading.attr="disabled">
    Save
    <span wire:loading>Saving..</span>
</x-button>
Enter fullscreen mode Exit fullscreen mode

So now "Saving.." will get displayed as long as the submit button is clicked. And it will get hidden when the AJAX Call has finished. However, during this AJAX Call both "Save" and "Saving.." are showing. So we also need to hide the "Save" during this AJAX Call. We can do so using wire:loading.remove

<x-button wire:click="save" wire:loading.attr="disabled">
    <span wire:loading.remove>Save</span>
    <span wire:loading>Saving..</span>
</x-button>
Enter fullscreen mode Exit fullscreen mode

Now, even though this is working, this will lead to unexpected issues when you have more than 1 button on the Page. So it is always a good practice to specify that we only want to change the display of these elements when the AJAX Call corresponding to save method is being called. We can do so using wire:target.

<x-button wire:click="save" wire:loading.attr="disabled">
    <span wire:loading.remove wire.target="save">Save</span>
    <span wire:loading wire.target="save">Saving..</span>
</x-button>
Enter fullscreen mode Exit fullscreen mode

At this stage, you should see your buttons having the same behaviour as shared at the start of the Article. However, we can further improve the readability of our code by extracting the code to our Button Component. We want this to be as simple as following:

<x-button wire:click="save" loading="Saving..">
  Save
</x-button>
Enter fullscreen mode Exit fullscreen mode

First of all we will create a loading property in our button component and assign it a default value of false.

@props(['loading' => false])
Enter fullscreen mode Exit fullscreen mode

We can read the Livewire Attributes inside our Blade Component using below code:

$attributes->wire('click')->value()
Enter fullscreen mode Exit fullscreen mode

You can read more about them in the Livewire Docs.

When both loading property is present and the wire:click attribute is present, we want to insert our span tags, otherwise we will just display the Slot. So our Blade Components becomes like this.

@props(['loading' => false])

<button {{ $attributes->merge(['type' => 'submit']) }}>
    @if ($loading && $target = $attributes->wire('click')->value())
        <span wire:loading.remove wire:target="{{$target}}">{{$slot}}</span>
        <span wire:loading wire:target="{{$target}}">{{$loading}}</span>
    @else
        {{ $slot }} 
    @endif
</button>
Enter fullscreen mode Exit fullscreen mode

So now we can use our Button Component using the loading attributes as well as without the loading attribute.

Hope you have enjoyed this tutorial. For similar articles, you can follow me on Twitter

Discussion (0)