Filter results 6 through 10 of 100 with ng-repeat in AngularJS

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?

89324 次浏览

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>

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.

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>

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++;

I started playing around with customer filters but then found out you can just call slice inside of the ng-repeat expression:

<div ng-repeat="item in items.slice(6, 10)">\{\{item}}</div>

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

@bluescreen's ans and @Brandon's ans are not equivalent.

for example:

var pageSize = 3;
var page = 2;
var items = [1, 2, 3, 4];

--

item in items | limitTo: page*pageSize | limitTo: -1*pageSize

produce [2, 3, 4]

item in items | slice: (page-1)*pageSize : page*pageSize

produce [4]

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
}


});