Saurabh Mahajan
Posted on June 3, 2021
In this Tutorial, we will implement Livewire Infinite Scrolling which will automatically Load Records from the Database as the User Scrolls to the bottom of the list. And unlike other examples that you might have seen, we will make sure our code is performant and do not fetch all the Records when the Component re-renders.
I am starting with Fresh Laravel Project with Livewire Installed. I have also installed Breeze, but that is not mandatory and entirely up to you. I have also set up a Route where we are going to display our Livewire Component. And finally, I have a Product Model which I have seeded with a few Records. We will be scrolling through the list of Products.
We can create the Livewire Component using the below Artisan Command.
php artisan make:livewire LoadProducts
We can render it in our View using the below command
@livewire('load-products')
And within this Livewire Component, I am going to define 2 properties, $page
and $perPage
. I am going to set these properties in the mount method.
<?php
namespace App\Http\Livewire;
use App\Models\Product;
use Livewire\Component;
class LoadProducts extends Component
{
public $perPage;
public $page;
public function mount($page = null, $perPage = null)
{
$this->page = $page ?? 1;
$this->perPage = $perPage ?? 10;
}
}
Since we didn't provided any values while rendering the Livewire Component, $page
and $perPage
will have default value of 1 and 10 respectively.
Next we are going to create a render method which will return the records corresponding to 1st Page and pass it to our View load-products
public function render()
{
$products = Product::paginate($this->perPage, ['*'], null, $this->page);
return view('livewire.products.load-products', [
'products' => $products
]);
}
Within Our View File which is located at resources/views/livewire/products/load-products.blade.php
we can simply loop through all the Products.
<div>
@foreach($productsas $result)
.
.
.
@endforeach
</div>
At this stage if you open up your route containing this Livewire Component, you will see first 10 records.
First of all we are going to use a Button to Load More Records. Once we get that working, we will switch to Scrolling.
Now we can add a button after our foreach
loop ends like below
<x-button wire:click="loadMore">Load More Products</x-button>
At the click of this Button, we need to fetch the next 10 records and append to the current list. However, Livewire renders the whole Component so we can use the append approach.
Second option is to fetch all the 20 records and render the component again. This will give an illusion of Loading More Data but it has serious performance issues.
Instead what we are going to do is we are going to create another Component, LoadMoreProducts
. This Component would be responsible for:
- Displaying the above Load More Products Button.
- Handling the Click Event which will fetch the Products for 2nd and subsequent Pages.
- Conditionally displaying the View Related to
LoadProducts
Component.
So we can create this Component using below command:
php artisan make:livewire LoadMoreProducts
We will add this Component in our View File at resources/views/livewire/products/load-products.blade.php
like below:
@if($products->hasMorePages())
@livewire('load-more-products', ['page' => $page, 'perPage' => $perPage, 'key' => 'products-page-' . $page])
@endif
Please see that we are displaying this Component only if there are more Products to be fetched. We are also passing the $page
and $perPage
Property. We are also using a unique key to identify it as there are going to be multiple of these components, 1 for each subsequent page.
Within our Component File located at app/Http/Livewire/LoadMoreProducts
, we will have the following code which at this stage is very similar to LoadProducts
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Product;
class LoadMoreProducts extends Component
{
public $perPage;
public $page;
public $loadMore = false;
public function mount($page = null, $perPage = null)
{
$this->page = $page ?? 1;
$this->perPage = $perPage ?? 10;
}
public function render()
{
return view('livewire.products.load-more-products');
}
}
Within the View File of this Component located at resources/views/livewire/products/load-more-products.blade.php
, we will eventually display our Button.
<x-button wire:click="loadMore">Load More Products</x-button>
As you can see that we are calling a loadMore
method at the click of this Button. We need to define this Method in LoadMoreProducts
Component and it will handle following functionalities:
- We will increment the
$page
Property by 1. - We will define a new Flag
loadMore
and set it to true. - Within the render method we will check the above flag. If this Flag is true, we will fetch the Records from the DB for the given
$page
and display the View Related toLoadProducts
Component. Otherwise, we will just display the View Related toLoadMoreProducts
, which renders the Button.
Here is how our LoadMoreProducts Component looks like
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Product;
class LoadMoreProducts extends Component
{
public $perPage;
public $page;
public $loadMore = false;
public function mount($page = null, $perPage = null)
{
$this->page = $page ?? 1;
$this->perPage = $perPage ?? 10;
}
public function loadMore()
{
$this->page += 1;
$this->loadMore = true;
}
public function render()
{
if (!$this->loadMore) {
return view('livewire.products.load-more-products');
} else {
$products = Product::paginate($this->perPage, ['*'], null, $this->page);
return view('livewire.products.load-products', [
'products' => $products
]);
}
}
}
So when the User asks for the 2nd Page, we are fetching the Products related to 2nd Page and displaying them via livewire.products.load-products
. This View is then also displaying the "Load More Products" Button for the 3rd Page.
This way by breaking the functionality to two components we have tackled the issue of appending to the current list.
Now let us make changes to load more Products as the User Scrolls rather than the Click of a Button. We only need to make changes in the View of LoadMoreProducts
. We need to fire the click event as the user Scrolls to the end of the List. We can do so by using x-init
Directive of the AlpineJs.
<div x-data="{
checkScroll() {
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
@this.call('loadMore')
}
};
}
}"
x-init="checkScroll"
>
Now as the User Scrolls to the end of the List, loadMore
Method will be triggered and Records would be appended to the end of the list. We will get the same output as shown in the image at the start of the Tutorial.
If you are interested in the code, you can get it from Github.
Hope you have enjoyed this tutorial. For similar articles, you can follow me on Twitter
Posted on June 3, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.