0

Paginated AngularJS posts application performance issue

I have made a small application that displays an posts JSON in the form of cards, with the help of AngularJS and Twitter Bootstrap 4.

The application has a pagination and there are about 100 posts displayed on each page.

var root = 'https://jsonplaceholder.typicode.com';

// Create an Angular module named "postsApp"
var app = angular.module("postsApp", []);

// Create controller for the "postsApp" module
app.controller("postsCtrl", ["$scope", "$http", "$filter", function($scope, $http, $filter) {
  var url = root + "/posts";
  $scope.postList = [];
  $scope.search = "";
  $scope.filterList = function() {
    var oldList = $scope.postList || [];
    $scope.postList = $filter('filter')($scope.posts, $scope.search);
    if (oldList.length != $scope.postList.length) {
      $scope.pageNum = 1;
      $scope.startAt = 0;
    };
    $scope.itemsCount = $scope.postList.length;
    $scope.pageMax = Math.ceil($scope.itemsCount / $scope.perPage);
  };
  $http.get(url)
    .then(function(data) {
      // posts arary
      $scope.posts = data.data;
      $scope.filterList();

      // Paginate
      $scope.pageNum = 1;
      $scope.perPage = 24;
      $scope.startAt = 0;
      $scope.filterList();

      $scope.currentPage = function(index) {
        $scope.pageNum = index + 1;
        $scope.startAt = index * $scope.perPage;
      };

      $scope.prevPage = function() {
        if ($scope.pageNum > 1) {
          $scope.pageNum = $scope.pageNum - 1;
          $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
        }
      };

      $scope.nextPage = function() {
        if ($scope.pageNum < $scope.pageMax) {
          $scope.pageNum = $scope.pageNum + 1;
          $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
        }
      };
    });
}]);
.posts-grid {
  margin-top: 25px;
  display: flex;
  flex-wrap: wrap;
}

.posts-grid>[class*='col-'] {
  display: flex;
  flex-direction: column;
  margin-bottom: 25px;
}

.posts-grid .post {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-top: 1px solid #d5d5d5;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.11);
}

.posts-grid .text {
  padding: 8px;
}

.posts-grid .card-title {
  font-size: 1.25rem;
  margin-bottom: 8px;
  text-transform: capitalize;
}

.posts-grid .read-more {
  padding: 0 8px 8px 8px;
  margin-top: auto;
}

.posts-grid .text-muted {
  margin-bottom: 8px;
}

.posts-grid .thumbnail img {
  display: block;
  width: 100%;
  height: auto;
}

.posts-grid p {
  text-align: justify;
}

.pagination>li>a,
.pagination>li>a:hover,
.pagination>li>span {
  color: #585858;
  line-height: 1;
  padding: 6px 12px;
  text-decoration: none;
}

.pagination>.active>a,
.pagination>.active>span,
.pagination>.active>a:hover,
.pagination>.active>span:hover,
.pagination>.active>a:focus,
.pagination>.active>span:focus {
  background-color: #007bff;
  border-color: #2b7c2b;
  color: #fff;
}

@media (max-width: 767px) {
  .container {
    max-width: 100%;
  }
}

@media (max-width: 575px) {
  .container {
    max-width: 100%;
    padding-left: 0;
    padding-right: 0;
  }
  .posts-grid>[class*='col-'] {
    padding-left: 5px;
    padding-right: 5px;
  }
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.js"></script>

<nav class="navbar navbar-expand-md bg-dark navbar-dark sticky-top">
  <!-- Brand -->
  <a class="navbar-brand" href="#">My Blog</a>
  <!-- Toggler/collapsibe Button -->
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
  <span class="navbar-toggler-icon"></span>
  </button>

  <!-- Navbar links -->
  <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav ml-auto">
      <li class="nav-item">
        <a class="nav-link active" href="#">Contacts</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">About us</a>
      </li>
      <li class="nav-item">
        <a class="nav-link btn btn-outline-primary" href="#">Login</a>
      </li>
    </ul>
  </div>
</nav>

<div data-ng-app="postsApp">
  <div class="container" data-ng-controller="postsCtrl">
    <div class="row">
      <div class="col-sm-9 mx-auto">
        <div class="form-group search-box mt-3 px-3">
          <input type="text" class="form-control" id="search" placeholder="Search post" data-ng-model="search" ng-change="filterList()">
        </div>
      </div>
    </div>
    <div class="posts-grid" ng-if="postList.length > 0">
      <div class="col-xs-12 col-sm-6 col-lg-4 col-xl-3" data-ng-repeat="post in postList | limitTo : perPage : startAt">
        <div class="post">
          <div class="thumbnail">
            <img src="//lorempixel.com/450/300" />
          </div>
          <div class="text">
            <h3 class="card-title">{{post.title}}</h3>
            <p class="text-muted">{{post.body}}</p>
          </div>
          <div class="read-more">
            <a class="btn btn-block btn-sm btn-primary" href="#">Read more</a>
          </div>
        </div>
      </div>
    </div>
    <p ng-if="postList.length <= 0" class="text-center">There are no posts</p>
    <div ng-if="pageMax > 1">
      <ul class="pagination pagination-sm justify-content-center">
        <li class="page-item"><a href="#" ng-click="prevPage()"><i class="fa fa-chevron-left"></i></a></li>
        <li ng-repeat="n in [].constructor(pageMax) track by $index" ng-class="{true: 'active'}[$index == pageNum - 1]">
          <a href="#" ng-click="currentPage($index)">{{$index+1}}</a>
        </li>
        <li><a href="#" ng-click="nextPage()"><i class="fa fa-chevron-right"></i></a></li>
      </ul>
    </div>
  </div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
<script>
  $(document).ready(function($) {
    $('.pagination > li > a').click(function() {
      $("html, body").animate({
        scrollTop: 0
      }, 500);
      return false;
    });
  });
</script>

A few questions ware born in my mind recently and I did not find the answer, hence my topic here:

  1. What if there ware 10 or 100 times as many posts, woud the application have a performance (page load) problem?

  2. Is there a better way to paginate the application; one that would load as many items from posts.json as there are displayed on one page (24 items), instead of the entire JSON file?

  3. How would you optimize this application, on the front end?
Razvan Zamfir
  • 4,209
  • 6
  • 38
  • 252

1 Answers1

1

What if there ware 10 or 100 times as many posts, woud the application have a performance (page load) problem?

Yes, if you load on front-end a lot of data browser could be go down. You can dimostrate it, need only create a series of stress test. Try to return hundred of hundred of data from your back-end (not jsonplaceholder), the browser will freeze

Is there a better way to paginate the application; one that would load as many items from posts.json as there are displayed on one page (24 items), instead of the entire JSON file?

In my experience I did pagination using a collaboration between front-end and back-end side.

front-end: you can choose different kind of pagination (infinity scroll, standard pagination etc.) in base of your features.

back-end: you can create rest service that take in input the items per page and an offset (your page).

When on browser you change page you will demande the back-end the honer to call a pageable query.

In base of my experience I gained a good performance with a lot of data.

Razvan Zamfir
  • 4,209
  • 6
  • 38
  • 252
carmelolg
  • 493
  • 5
  • 17
  • How would you modify this application, on the front end? – Razvan Zamfir Jul 28 '18 at 10:26
  • 1
    I found online that jsonplaceholder have already implemented the pagination from BE. You should call this kind of rest `http://jsonplaceholder.typicode.com/posts?_start=5&_limit=10` (ten element from position 5 to 15 (5, the offset + 10 items per page). And then create a function on the controller that have the honer to call this service everytime you need to change page (passing itemsPerPage and offset). If you want I could give you an example, but I need a bit time. – carmelolg Jul 28 '18 at 10:56
  • 1
    @RazvanZamfir maybe this link (http://fdietz.github.io/recipes-with-angular-js/common-user-interface-patterns/paginating-through-server-side-data.html) could help you. Should be similar to my idea – carmelolg Jul 28 '18 at 10:59