How to Create an Ajax Pagination Using Laravel

7 years ago by Filip Zdravkovic

Ajax Pagination using Laravel Laraget.com

Warning: This post is over a year old, so some of this information may be out of date (Laravel 5.2 and Bootstrap 3 were used).

Demo

In this article I will demonstrate one way to create an Ajax pagionation using Laravel and jQuery. In order to understand this post, you need to be familiar with standard (non-Ajax) Laravel's paginator (how it works), and you should also have a basic knowledge of Ajax and jQuery.

Let's say that we have a simple Laravel webiste with articles and we want to have Ajax pagination on the page that shows (lists) all of the articles (for example, this page can be accessed from www.example.com/articles and it displays paginated lists of article titles where each of these titles is a link):

Listing articles with Laravel's paginatorFirst, let’s define a partial view in resources/views/articles/load.blade.php for displaying pagination results:

<div id="load" style="position: relative;">
@foreach($articles as $article)
    <div>
        <h3>
            <a href="{{ action('ArticleController@show', [$article->id]) }}">{{$article->title }}</a>
        </h3>
    </div>
@endforeach
</div>
{{ $articles->links() }}


Note that there is {{ $articles->links() }} at the bottom - the Laravel’s links method will render the links to the rest of the pages in the result set. Each of these links will already contain the proper page query string variable. As for the <div id="load" style="position: relative;"> - you’ll see its purpose later.

This partial view will be rendered and returned as a response to a jQuery Ajax Request. Let's get this straight: when the user clicks on the pagination link (number) - an Ajax request containing URI with the proper page query string variable (page number) will be sent to the index method that is defined in ArticleController. So, let's see how that index method looks like:

class ArticleController extends Controller
{
    protected $articles;

    public function __construct(Article $articles)
    {
        $this->articles = $articles;
    }


    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $articles = $this->articles->latest('created_at')->paginate(5);

        if ($request->ajax()) {
            return view('articles.load', ['articles' => $articles])->render();  
        }

        return view('articles.index', compact('articles'));
    }


As you can see,  in the index method we first use the paginate method on an Eloquent query:

$articles = $this->articles->latest('created_at')->paginate(5);


The paginate method provided by Laravel automatically takes care of setting the proper limit and offset based on the current page being viewed by the user. By default, the current page is detected by the value of the ?page query string argument on the HTTP request. Of course, this value is automatically detected by Laravel, and is also automatically inserted into links generated by the paginator1. Those paginated results are stored in the $articles variable.

Next, we check if the received request is actually an Ajax request:

        if ($request->ajax()) {
            return view('articles.load', ['articles' => $articles])->render();  
        }


If it is, we will pass the $articles variable to the partial view defined in resources/views/articles/load.blade.php, render it and return as HTML response.

Finally, we want  to make sure that our website is working if JavaScript is disabled; in that case we’ll have a standard (non-Ajax) pagination:

return view('articles.index', compact('articles'));


As you can see, we pass $articles into the view resources/views/articles/index.blade.php and return it.

Now, let’s define that view located in resources/views/articles/index.blade.php:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta charset="UTF-8">
    <title>Larave Ajax Pagination</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="row">
            
            <div class="col-sm-9">
                @if (count($articles) > 0)
                    <section class="articles">
                        @include('articles.load')
                    </section>
                @endif
            </div>

            <div class="col-sm-3">
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>


Note that we are including partial view resources/views/articles/load.blade.php that we defined earlier. Now, if we register a resourceful route in app/Http/routes.php (or if you are using Laravel 5.3 in routes/web.php):

Route::resource('articles', 'ArticleController');

we will have a standard (non-Ajax) pagination that is working (you can test it).

The next step is to put the following jQuery just above </body>:

<script type="text/javascript">

$(function() {
    $('body').on('click', '.pagination a', function(e) {
        e.preventDefault();

        $('#load a').css('color', '#dfecf6');
        $('#load').append('<img style="position: absolute; left: 0; top: 0; z-index: 100000;" src="/images/loading.gif" />');

        var url = $(this).attr('href');  
        getArticles(url);
        window.history.pushState("", "", url);
    });

    function getArticles(url) {
        $.ajax({
            url : url  
        }).done(function (data) {
            $('.articles').html(data);  
        }).fail(function () {
            alert('Articles could not be loaded.');
        });
    }
});

</script>


Let’s analyze this script:

$('body').on('click', '.pagination a', function(e) { - here we attach an event handler function to the "click" event (to the click on the pagination link). And, when a user clicks on the pagination link - the default action of the event will not be triggered [e.preventDefault();].

Next, we temporarily change the color of the listed titles of articles (which are links) and we append loading.gif image (with an absolute position) inside of <div id="load" style="position: relative;"> (which is in resources/views/articles/load.blade.php that we defined earlier).

var url = $(this).attr('href'); - this will get the value of the href attribute of the pagination link that was clicked. For example, if the user clicked on the number 2 link - the value of the url variable would be http://example.com/articles?page=2.

Finally, we are passing this URL to the getArticles function which just sends an HTTP (Ajax) request to that URL. And since we defined a resourceful route in app/Http/routes.php (or if you are using Laravel 5.3 in routes/web.php) - we are actually sending Ajax request to the index method defined in ArticleController. If everything is ok (if the index method returned rendered HTML response) - we just need to set the HTML contents: $('.articles').html(data); If something went wrong - you can do whatever you want, in my case an alert box pops up with the message “Articles could not be loaded”.

Lastly, there is window.history.pushState("", "", url); to keep (show) pagination URLs in the address bar so that the users can bookmark or share the links...

So, here is the final version of resources/views/articles/index.blade.php:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta charset="UTF-8">
    <title>Laravel Ajax Pagination</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-sm-9">

                @if (count($articles) > 0)
                    <section class="articles">
                        @include('articles.load')
                    </section>
                @endif

            </div>

            <div class="col-sm-3">
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

    <script type="text/javascript">

        $(function() {
            $('body').on('click', '.pagination a', function(e) {
                e.preventDefault();

                $('#load a').css('color', '#dfecf6');
                $('#load').append('<img style="position: absolute; left: 0; top: 0; z-index: 100000;" src="/images/loading.gif" />');

                var url = $(this).attr('href');
                getArticles(url);
                window.history.pushState("", "", url);
            });

            function getArticles(url) {
                $.ajax({
                    url : url
                }).done(function (data) {
                    $('.articles').html(data);
                }).fail(function () {
                    alert('Articles could not be loaded.');
                });
            }
        });
    </script>
</body>
</html>


And that’s it. Click here for a small demo. If you know a better or a more elegant way to implement this, please let me know.

Warning: This post is over a year old, so some of this information may be out of date (Laravel 5.2 and Bootstrap 3 were used).


View All Blog Posts Here