Ben Osborne
Ben Osborne

Reputation: 1542

Angular - Adjust Pagination When Filtered

I'm new to Angular, so go easy on me.

My question is similar to this one. I'm doing something a little different, so the accepted answer isn't working for me.

I'm using Angular UI Bootstrap's Pagination directive to paginate a dataset, and I'm using Angular filters to allow users to filter data for any column.

The problem is that when the data is filtered, the user only sees the data on the page he's currently on.

$scope.applyFilter = function() {
    var begin = (($scope.currentPage - 1) * $scope.itemsPerPage);
    var end = begin + $scope.itemsPerPage;
    $scope.totalItems = $filter('filter')($scope.items).length;
    $scope.filteredItems = $filter('filter')($scope.items).slice(begin, end);
};

Here's the full Plunker

For example, if you key in a state of "GA" in the state filter (text box under the word "State"), no data is returned. However, you can select page 4 and see the one record that meets the condition.

What I would like to happen is for the pagination to automatically adjust, showing just one page of results with the correct record appearing to the user.

I know that there are other grid directives that already have this functionality, but I'd prefer to not completely abandon my current approach.

Upvotes: 0

Views: 1200

Answers (3)

rob
rob

Reputation: 18513

You can use the limitTo filter for pagination in your ng-repeat.

e.g.

<tr ng-repeat="item in items | filter:search:strict | orderBy:propertyName:reverse | limitTo:5:(currentPage-1)*5" >

The tricky part is updating the number of pages to show in your pagination widget when the filter changes. You can do this by assigning the filtered items to a variable before the limitTo filter is applied. e.g.

<tr ng-repeat="item in filteredItems = (items | filter:search:strict | orderBy:propertyName:reverse) | limitTo:5:(currentPage-1)*5" >

<ul uib-pagination total-items="filteredItems.length" ...></ul>

With this approach there is no need to $watch anything or do any filtering in your controller.

Working plunkr: http://plnkr.co/edit/8NinIYdRsXDVLCPz5VJN?p=preview

Upvotes: 1

Dennis Baizulin
Dennis Baizulin

Reputation: 1420

I suggest you writing a custom pagination filter

app.filter('paginate', function() {
    return function(items, currentPage, itemsPerPage) {
        if (!Array.isArray(items)) {
            return items;
        }

        var sliceStart = (currentPage - 1) * itemsPerPage;

        return items.slice(sliceStart, sliceStart + itemsPerPage);
    };
});

Moving filtering logics to the controller

$scope.applyFilter = function() {
    var begin = (($scope.currentPage - 1) * $scope.itemsPerPage);
    var end = begin + $scope.itemsPerPage;

    $scope.filteredItems = $filter('filter')($scope.items, $scope.search);
    $scope.filteredItems = $filter('orderBy')($scope.filteredItems, $scope.propertyName, $scope.reverse);
};

And finally updating your HTML in two places. Here

<tr ng-repeat="item in filteredItems | paginate : currentPage : itemsPerPage">

and here

<ul uib-pagination total-items="filteredItems.length" ng-model="currentPage" class="pagination-sm" boundary-links="true" force-ellipses="true" items-per-page="5" ></ul>

Your updated example

Upvotes: 1

Varkal
Varkal

Reputation: 1378

You forgot the parameter into your filters call

$scope.applyFilter = function() {
    var begin = (($scope.currentPage - 1) * $scope.itemsPerPage);
    var end = begin + $scope.itemsPerPage;
    $scope.totalItems = $filter('filter')($scope.items,  $scope.search).length;
    $scope.filteredItems = ($filter('filter')($scope.items, $scope.search)).slice(begin, end);
};

Upvotes: 1

Related Questions