Search code examples
laravellaravel-livewirealpine.js

AlpineJS Load More Animation


I am running into an issue where the load more animation is not getting fired when I have the index set to 10 and I increment by 10, however if i switch the index to 9 and the increment to 9 it runs just fine. On the backend there is a total of 20 so Ideally 10 start on the page and you can load the additional ten or more by clicking the button. I am hoping someone can help me understand why ten doesnt work but 9 does:

<div x-data="{ show: false, index: 10 }">
<div class="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-5 gap-4 md:gap-6 lg:gap-6 mt-4">
    @foreach($tributes as $tribute)
        <div class="gallery-border p-8" x-show="show && index > {{$loop->index}}" x-transition:enter="transition ease-out duration-500" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100">
            <p class="text-2xl md:text-4xl lg:text-4xl">{{ $tribute->message }}</p>
            <p class="text-sm">-{{ $tribute->name }}</p>
        </div>
    @endforeach
</div>
@if(count($tributes) < $totalTributes)
    <div class="text-center mt-4">
        @if($totalTributes - count($tributes) < 10)
            <button x-on:click="show = true; index = index + {{$totalTributes - count($tributes)}}; $wire.loadMore()" x-init="setTimeout(() => { show = true }, 500)" class="tributes-gallery-load-more">Load More</button>
        @else
            <button x-on:click="show = true; index = index + 10; $wire.loadMore()" x-init="setTimeout(() => { show = true }, 500)" class="tributes-gallery-load-more">Load More</button>
        @endif
    </div>
@endif
<?php

namespace App\Http\Livewire;

use App\Models\Tribute;
use Livewire\Component;

class TributeGallery extends Component
{
    public $tributes, $totalTributes;
    public $perPage = 10;
    public $page = 1;

    public function loadMore()
    {
        $this->page++;
        $newTributes = Tribute::where('status', 'approved')
                              ->orderBy('id', 'desc')
                              ->skip(($this->page - 1) * $this->perPage)
                              ->take($this->perPage)
                              ->get();

        $this->tributes = $this->tributes->concat($newTributes);

    }

    public function mount()
    {
        $this->totalTributes = Tribute::where('status', 'approved')->count();
        $this->tributes = Tribute::where('status', 'approved')
                                  ->orderBy('id', 'desc')
                                  ->take($this->perPage)
                                  ->get();

    }


    public function render()
    {

        return view('livewire.tribute-gallery');
    }
}

Solution

  • On the first page load with a half second delay you switch show to true. You never switch back this variable to false so half of the x-show condition is always satisfied for the subsequent loads. The index starts from 10 and when you click on the Load More button first the index variable is increased by 10, so now its 20 and show is still true. After that you call $wire.loadMore() to load the next batch. However since you have already increased the index variable and again show remains true for the whole time, the x-show="show && index > {{$loop->index}}" condition already satisfied when Livewire loads the next batch, so Alpine.js will not fire the transition animation since the transition will not occur.

    The fix is now straightforward: load the next batch first and just after that increase the index variable (optionally with a small delay), so the x-show condition can change from false to true for the new items therefore the transition can occur as well. The show variable is redundant and can be removed.

    <div x-data="{ index: 0 }">
    <div class="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-5 gap-4 md:gap-6 lg:gap-6 mt-4">
        @foreach($tributes as $tribute)
            <div class="gallery-border p-8" x-show="index > {{$loop->index}}" x-transition:enter="transition ease-out duration-500" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100">
                <p class="text-2xl md:text-4xl lg:text-4xl">{{ $tribute->message }}</p>
                <p class="text-sm">-{{ $tribute->name }}</p>
            </div>
        @endforeach
    </div>
    @if(count($tributes) < $totalTributes)
        <div class="text-center mt-4">
          <button x-on:click="$wire.loadMore(); setTimeout(() => { index = index + 10 }, 500)" class="tributes-gallery-load-more">Load More</button>
        </div>
    @endif
    </div>