I see the limitTo
filter in the docs, which allows me to limit the first 5, or last 5 results, but I want to set where my limit starts so I can show the second set of 5 results.
Is there a built in filter for that?
Since Angular 1.4.0, the limitTo
filter takes an optional begin
argument:
<div ng-repeat="item in items | limitTo:5:5">{{item}}</div>
In older versions, writing a custom filter is fairly straightforward. Here's a naïve implementation based on Array#slice
(note you pass the first and last index, instead of a count):
app.filter('slice', function() {
return function(arr, start, end) {
return (arr || []).slice(start, end);
};
});
<div ng-repeat="item in items | slice:6:10">{{item}}</div>
Working jsFiddle: http://jsfiddle.net/BinaryMuse/vQUsS/
Alternatively, you can simply steal the entire Angular 1.4.0 implementation of limitTo
:
function limitToFilter() {
return function(input, limit, begin) {
if (Math.abs(Number(limit)) === Infinity) {
limit = Number(limit);
} else {
limit = toInt(limit);
}
if (isNaN(limit)) return input;
if (isNumber(input)) input = input.toString();
if (!isArray(input) && !isString(input)) return input;
begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
if (limit >= 0) {
return input.slice(begin, begin + limit);
} else {
if (begin === 0) {
return input.slice(limit, input.length);
} else {
return input.slice(Math.max(0, begin + limit), begin);
}
}
};
}
AngularJS provides that functionality already out of the box. If you carefully read the limitTo documentation it allows you to specify a negative value for the limit. That means N elements at the end so if you want to process 5 results after an offset of 5 you need to do the following:
<div ng-repeat="item in items | limitTo: 10 | limitTo: -5">{{item}}</div>
As bluescreen said, it can be done using only the limitTo
filter, although dealing with the last page problem noticed by Harry Oosterveen needs some extra work.
I.e. using ui-bootstrap pagination
directive properties:
ng-model = page // current page
items-per-page = rpp // records per page
total-items = count // total number of records
The expression should be:
<div ng-repeat="item in items | limitTo: rpp * page | limitTo: rpp * page < count ? -rpp : rpp - (rpp * page - count)">{{item}}</div>
Here's a functional example on how to filter a list with offset and limit:
<!doctype html>
<html lang="en" ng-app="phonecatApp">
<head>
<script src="lib/angular/angular.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
Offset: <input type="text" ng-model="offset" value="0" /><br />
Limit: <input type="text" ng-model="limit" value="10" /><br />
<p>offset limitTo: {{offset - phones.length}}</p>
<p>Partial list:</p>
<ul>
<li ng-repeat="phone in phones | limitTo: offset - phones.length | limitTo: limit">
{{$index}} - {{phone.name}} - {{phone.snippet}}
</li>
</ul>
<hr>
<p>Whole list:</p>
<ul>
<li ng-repeat="phone in phones">
{{$index}} - {{phone.name}} - {{phone.snippet}}
</li>
</ul>
</body>
</html>
The example is based on the second step of AngularJS tutorial.
Note that the filter for the offset has to be put as the first filter if you want to cover the generic case where the limit is uknnown.
from bluescreen answer :
<div ng-repeat="item in items | limitTo: 10 | limitTo: -5">{{item}}</div>
you might also find a scenario where you need to take N-th item till the last item, this is another way using limitTo:
<div ng-repeat="item in items | limitTo:-(items.length-5)>{{item}}</div>
negative sign will take as much as items.length from last, then minus 5 inside the bracket will skip the first 5 items
There's a tidier way to do it without having to invoke a filter and will behave more like a paginator:
$scope.page = 0;
$scope.items = [ "a", "b", "c", "d", "e", "f", "g" ];
$scope.itemsLimit = 5;
$scope.itemsPaginated = function () {
var currentPageIndex = $scope.page * $scope.itemsLimit;
return $scope.items.slice(
currentPageIndex,
currentPageIndex + $scope.itemsLimit);
};
And just stick that in your view:
<ul>
<li ng-repeat="item in itemsPaginated() | limitTo:itemsLimit">{{item}}</li>
</ul>
Then you can just increment/decrement $scope.page
:
$scope.page++;
You can also create your own reusable filter startFrom like so:
myApp.filter('startFrom', function () {
return function (input, start) {
start = +start;
return input.slice(start);
}
});
and then use it in your ng-repeat like so:
ng-repeat="item in items | startFrom: 5 | limitTo:5
That way it is done "in a spirit of AngularJs filters", testable, can have some "extra" logic if needed, etc.
For Ionic based projects find the solution below:
mainpage.html:
<ion-view view-title="My Music">
<ion-content>
<ion-list>
<ion-item ng-repeat="track in tracks | limitTo: limit | limitTo: -10 ">
{{track.title}}
</ion-item>
</ion-list>
<div class="list"></div>
<div align="center">
<button class="button button-small button-positive" ng-disabled="currentPage == 0" ng-click="decrementLimit()">
Back
</button>
<button class="button button-small button-energized">
{{currentPage+1}}/{{numberOfPages()}}
</button>
<button class="button button-small button-positive" ng-disabled="currentPage >= data_length/pageSize - 1" ng-click="incrementLimit()">
Next
</button>
</div>
</ion-content>
</ion-view>
controller.js:
var myApp = angular.module('musicApp.controllers', []);
myApp.controller('AppCtrl', function($scope, $http) {
console.log('AppCtrl called');
// $scope.tracks = MusicService.query();
$http.get('api/to/be/called').then(function(result){
$scope.tracks = result.data['results'];
$scope.data_length = $scope.tracks.length;
console.log(result)
// console.log($sco pe.tracks.length)
});
var currentPage = 0;
$scope.pageSize = 10;
$scope.numberOfPages = function(){
return Math.ceil($scope.tracks.length/$scope.pageSize);
}
var limitStep = 10;
$scope.limit = limitStep;
$scope.currentPage = currentPage;
$scope.incrementLimit = function(){
$scope.limit += limitStep;
$scope.currentPage += 1
};
$scope.decrementLimit = function(){
$scope.limit -= limitStep;
$scope.currentPage -= 1
}
});