Paginated AngularJS posts application
up vote
2
down vote
favorite
I have made a small application that displays a 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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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:
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
- 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?
- How would you optimize this application, on the front end?
javascript performance angular.js ajax pagination
add a comment |
up vote
2
down vote
favorite
I have made a small application that displays a 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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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:
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
- 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?
- How would you optimize this application, on the front end?
javascript performance angular.js ajax pagination
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I have made a small application that displays a 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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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:
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
- 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?
- How would you optimize this application, on the front end?
javascript performance angular.js ajax pagination
I have made a small application that displays a 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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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:
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
- 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?
- How would you optimize this application, on the front end?
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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>javascript performance angular.js ajax pagination
javascript performance angular.js ajax pagination
edited yesterday
Sᴀᴍ Onᴇᴌᴀ
8,07461751
8,07461751
asked Sep 4 at 10:08
Razvan Zamfir
859
859
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
Try it with 10 times as many posts. I know that just duplicates each post 10 times without modifying the unique values like id but it should demonstrate the performance. I can see the page links but unless I make the window full screen and zoom out (on my 24" monitor) I can't see the all the page links - e.g. the link for Page 1 is cut off, as well as anything beyond page 42.
- 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?
Unless the API endpoint accepts a parameter for the page size, you would likely need to utilize a server-side scripting language like Python, Ruby on Rails, PHP, C#, VB.NET, ASP, etc. which could return the specified pagesize.
- How would you optimize this application, on the front end?
I don't see much that stands out as an obvious place to optimize, though I do notice that the promise callback to $http.get(url) has two calls to filterList() separated by three assignment lines:
$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();
Is there any way to reduce the two calls to a single call there? Perhaps the first call is not needed?
Then looking at that promise callback, I see the three methods set on $scope there: currentPage, prevPage and nextPage. I would recommend moving those methods out of the callback handler, so that all it does is set the post data on $scope and related paging variables.
Also, I see a flaw with the jQuery code used for scrolling to the top (which doesn't appear to be working because there are no elements matching the selector .pagination > li > a when the DOM is loaded). I tried finding an equivalent in AngularJS but found little... There is a solution I found from this post that uses vanillaJS. I had to alter the href attributes on the anchors to use it.
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
Then that can be used in the click handler methods:
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
Though in order to stop the anchor navigation, the default event handling will need to be prevented:
<a href="#" ng-click="currentPage($index); $event.preventDefault();">{{$index+1}}</a>
Updated code
See modified code below.
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);
};
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
$scope.prevPage = function() {
if ($scope.pageNum > 1) {
$scope.pageNum = $scope.pageNum - 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.nextPage = function() {
if ($scope.pageNum < $scope.pageMax) {
$scope.pageNum = $scope.pageNum + 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
$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();
});
}]);.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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>
<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>
<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(); $event.preventDefault();"><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); $event.preventDefault();">{{$index+1}}</a>
</li>
<li><a href="#" ng-click="nextPage(); $event.preventDefault();"><i class="fa fa-chevron-right"></i></a></li>
</ul>
</div>
</div>
</div>add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
Try it with 10 times as many posts. I know that just duplicates each post 10 times without modifying the unique values like id but it should demonstrate the performance. I can see the page links but unless I make the window full screen and zoom out (on my 24" monitor) I can't see the all the page links - e.g. the link for Page 1 is cut off, as well as anything beyond page 42.
- 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?
Unless the API endpoint accepts a parameter for the page size, you would likely need to utilize a server-side scripting language like Python, Ruby on Rails, PHP, C#, VB.NET, ASP, etc. which could return the specified pagesize.
- How would you optimize this application, on the front end?
I don't see much that stands out as an obvious place to optimize, though I do notice that the promise callback to $http.get(url) has two calls to filterList() separated by three assignment lines:
$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();
Is there any way to reduce the two calls to a single call there? Perhaps the first call is not needed?
Then looking at that promise callback, I see the three methods set on $scope there: currentPage, prevPage and nextPage. I would recommend moving those methods out of the callback handler, so that all it does is set the post data on $scope and related paging variables.
Also, I see a flaw with the jQuery code used for scrolling to the top (which doesn't appear to be working because there are no elements matching the selector .pagination > li > a when the DOM is loaded). I tried finding an equivalent in AngularJS but found little... There is a solution I found from this post that uses vanillaJS. I had to alter the href attributes on the anchors to use it.
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
Then that can be used in the click handler methods:
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
Though in order to stop the anchor navigation, the default event handling will need to be prevented:
<a href="#" ng-click="currentPage($index); $event.preventDefault();">{{$index+1}}</a>
Updated code
See modified code below.
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);
};
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
$scope.prevPage = function() {
if ($scope.pageNum > 1) {
$scope.pageNum = $scope.pageNum - 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.nextPage = function() {
if ($scope.pageNum < $scope.pageMax) {
$scope.pageNum = $scope.pageNum + 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
$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();
});
}]);.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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>
<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>
<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(); $event.preventDefault();"><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); $event.preventDefault();">{{$index+1}}</a>
</li>
<li><a href="#" ng-click="nextPage(); $event.preventDefault();"><i class="fa fa-chevron-right"></i></a></li>
</ul>
</div>
</div>
</div>add a comment |
up vote
0
down vote
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
Try it with 10 times as many posts. I know that just duplicates each post 10 times without modifying the unique values like id but it should demonstrate the performance. I can see the page links but unless I make the window full screen and zoom out (on my 24" monitor) I can't see the all the page links - e.g. the link for Page 1 is cut off, as well as anything beyond page 42.
- 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?
Unless the API endpoint accepts a parameter for the page size, you would likely need to utilize a server-side scripting language like Python, Ruby on Rails, PHP, C#, VB.NET, ASP, etc. which could return the specified pagesize.
- How would you optimize this application, on the front end?
I don't see much that stands out as an obvious place to optimize, though I do notice that the promise callback to $http.get(url) has two calls to filterList() separated by three assignment lines:
$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();
Is there any way to reduce the two calls to a single call there? Perhaps the first call is not needed?
Then looking at that promise callback, I see the three methods set on $scope there: currentPage, prevPage and nextPage. I would recommend moving those methods out of the callback handler, so that all it does is set the post data on $scope and related paging variables.
Also, I see a flaw with the jQuery code used for scrolling to the top (which doesn't appear to be working because there are no elements matching the selector .pagination > li > a when the DOM is loaded). I tried finding an equivalent in AngularJS but found little... There is a solution I found from this post that uses vanillaJS. I had to alter the href attributes on the anchors to use it.
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
Then that can be used in the click handler methods:
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
Though in order to stop the anchor navigation, the default event handling will need to be prevented:
<a href="#" ng-click="currentPage($index); $event.preventDefault();">{{$index+1}}</a>
Updated code
See modified code below.
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);
};
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
$scope.prevPage = function() {
if ($scope.pageNum > 1) {
$scope.pageNum = $scope.pageNum - 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.nextPage = function() {
if ($scope.pageNum < $scope.pageMax) {
$scope.pageNum = $scope.pageNum + 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
$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();
});
}]);.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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>
<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>
<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(); $event.preventDefault();"><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); $event.preventDefault();">{{$index+1}}</a>
</li>
<li><a href="#" ng-click="nextPage(); $event.preventDefault();"><i class="fa fa-chevron-right"></i></a></li>
</ul>
</div>
</div>
</div>add a comment |
up vote
0
down vote
up vote
0
down vote
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
Try it with 10 times as many posts. I know that just duplicates each post 10 times without modifying the unique values like id but it should demonstrate the performance. I can see the page links but unless I make the window full screen and zoom out (on my 24" monitor) I can't see the all the page links - e.g. the link for Page 1 is cut off, as well as anything beyond page 42.
- 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?
Unless the API endpoint accepts a parameter for the page size, you would likely need to utilize a server-side scripting language like Python, Ruby on Rails, PHP, C#, VB.NET, ASP, etc. which could return the specified pagesize.
- How would you optimize this application, on the front end?
I don't see much that stands out as an obvious place to optimize, though I do notice that the promise callback to $http.get(url) has two calls to filterList() separated by three assignment lines:
$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();
Is there any way to reduce the two calls to a single call there? Perhaps the first call is not needed?
Then looking at that promise callback, I see the three methods set on $scope there: currentPage, prevPage and nextPage. I would recommend moving those methods out of the callback handler, so that all it does is set the post data on $scope and related paging variables.
Also, I see a flaw with the jQuery code used for scrolling to the top (which doesn't appear to be working because there are no elements matching the selector .pagination > li > a when the DOM is loaded). I tried finding an equivalent in AngularJS but found little... There is a solution I found from this post that uses vanillaJS. I had to alter the href attributes on the anchors to use it.
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
Then that can be used in the click handler methods:
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
Though in order to stop the anchor navigation, the default event handling will need to be prevented:
<a href="#" ng-click="currentPage($index); $event.preventDefault();">{{$index+1}}</a>
Updated code
See modified code below.
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);
};
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
$scope.prevPage = function() {
if ($scope.pageNum > 1) {
$scope.pageNum = $scope.pageNum - 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.nextPage = function() {
if ($scope.pageNum < $scope.pageMax) {
$scope.pageNum = $scope.pageNum + 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
$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();
});
}]);.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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>
<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>
<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(); $event.preventDefault();"><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); $event.preventDefault();">{{$index+1}}</a>
</li>
<li><a href="#" ng-click="nextPage(); $event.preventDefault();"><i class="fa fa-chevron-right"></i></a></li>
</ul>
</div>
</div>
</div>
- What if there ware 10 or 100 times as many posts, would the application have a performance (page load) problem?
Try it with 10 times as many posts. I know that just duplicates each post 10 times without modifying the unique values like id but it should demonstrate the performance. I can see the page links but unless I make the window full screen and zoom out (on my 24" monitor) I can't see the all the page links - e.g. the link for Page 1 is cut off, as well as anything beyond page 42.
- 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?
Unless the API endpoint accepts a parameter for the page size, you would likely need to utilize a server-side scripting language like Python, Ruby on Rails, PHP, C#, VB.NET, ASP, etc. which could return the specified pagesize.
- How would you optimize this application, on the front end?
I don't see much that stands out as an obvious place to optimize, though I do notice that the promise callback to $http.get(url) has two calls to filterList() separated by three assignment lines:
$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();
Is there any way to reduce the two calls to a single call there? Perhaps the first call is not needed?
Then looking at that promise callback, I see the three methods set on $scope there: currentPage, prevPage and nextPage. I would recommend moving those methods out of the callback handler, so that all it does is set the post data on $scope and related paging variables.
Also, I see a flaw with the jQuery code used for scrolling to the top (which doesn't appear to be working because there are no elements matching the selector .pagination > li > a when the DOM is loaded). I tried finding an equivalent in AngularJS but found little... There is a solution I found from this post that uses vanillaJS. I had to alter the href attributes on the anchors to use it.
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
Then that can be used in the click handler methods:
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
Though in order to stop the anchor navigation, the default event handling will need to be prevented:
<a href="#" ng-click="currentPage($index); $event.preventDefault();">{{$index+1}}</a>
Updated code
See modified code below.
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);
};
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
$scope.prevPage = function() {
if ($scope.pageNum > 1) {
$scope.pageNum = $scope.pageNum - 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.nextPage = function() {
if ($scope.pageNum < $scope.pageMax) {
$scope.pageNum = $scope.pageNum + 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
$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();
});
}]);.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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>
<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>
<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(); $event.preventDefault();"><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); $event.preventDefault();">{{$index+1}}</a>
</li>
<li><a href="#" ng-click="nextPage(); $event.preventDefault();"><i class="fa fa-chevron-right"></i></a></li>
</ul>
</div>
</div>
</div>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);
};
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
$scope.prevPage = function() {
if ($scope.pageNum > 1) {
$scope.pageNum = $scope.pageNum - 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.nextPage = function() {
if ($scope.pageNum < $scope.pageMax) {
$scope.pageNum = $scope.pageNum + 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
$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();
});
}]);.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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>
<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>
<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(); $event.preventDefault();"><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); $event.preventDefault();">{{$index+1}}</a>
</li>
<li><a href="#" ng-click="nextPage(); $event.preventDefault();"><i class="fa fa-chevron-right"></i></a></li>
</ul>
</div>
</div>
</div>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);
};
$scope.currentPage = function(index) {
$scope.pageNum = index + 1;
$scope.startAt = index * $scope.perPage;
$scope.scrollToTop();
};
$scope.prevPage = function() {
if ($scope.pageNum > 1) {
$scope.pageNum = $scope.pageNum - 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.nextPage = function() {
if ($scope.pageNum < $scope.pageMax) {
$scope.pageNum = $scope.pageNum + 1;
$scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
$scope.scrollToTop();
}
};
$scope.scrollToTop = function() {
var scrollDuration = 500;
var scrollStep = -window.scrollY / (scrollDuration / 10);
var scrollInterval = setInterval(function() {
if (window.scrollY != 0) {
window.scrollBy(0, scrollStep);
} else {
clearInterval(scrollInterval);
}
}, 15);
};
$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();
});
}]);.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 {
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;
}
.posts-grid .text-muted {
margin-bottom: 8px;
}
.posts-grid .thumbnail img {
display: block;
width: 100%;
height: auto;
}
.posts-grid p {
text-align: justify;
}
.posts-grid .post {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.posts-grid .read-more {
margin-top: auto;
}
.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>
<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>
<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(); $event.preventDefault();"><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); $event.preventDefault();">{{$index+1}}</a>
</li>
<li><a href="#" ng-click="nextPage(); $event.preventDefault();"><i class="fa fa-chevron-right"></i></a></li>
</ul>
</div>
</div>
</div>answered yesterday
Sᴀᴍ Onᴇᴌᴀ
8,07461751
8,07461751
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f203089%2fpaginated-angularjs-posts-application%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown