Angular pagination not updating when bound list changes due to filtering on an input text box
Asked Answered
E

3

6

Here's the scenario:

I am using an ASP.NET MVC site with Angular JS and Boostrap UI. I have a dynamic ul list populated by data fed through a controller call to AngularJS, filtering on that list through an input search box. The list is also controlled through pagination (UI Bootstrap control) that I've setup to show 10 results per page for the list of 100 or so items. This list is filtered as the user types in the search box, however I would like the pagination to update as well so consider the following example:

The list has 10 pages of items (100 items), the user types some text in the input search box which filters the list down to 20 or so items, so the pagination should be updated from 10 pages to two pages.

I figure there must be a $watch setup somewhere, perhaps on the list items after it has been filtered and then update the pagination page count, however I'm pretty new to AngularJS so can someone please explain to me how this could be done?

Thanks very much. I have posted my code below:

<div data-ng-app="dealsPage">
<input type="text" data-ng-model="cityName" />
<div data-ng-controller="DestinationController">
    <ul>
        <li data-ng-repeat="deals in destinations | filter: cityName |
            startFrom:currentPage*pageSize | limitTo:pageSize">{{deals.Location}}</li>
    </ul>
    <br />
    <pagination rotate="true" num-pages="noOfPages" current-page="currentPage" 
                max-size="maxSize" class="pagination-small" boundary-links="true"></pagination>
</div>

 var destApp = angular.module('dealsPage', ['ui.bootstrap', 'uiSlider']);
destApp.controller('DestinationController', function ($scope, $http) {
    $scope.destinations = {};
    $scope.currentPage = 1;
    $scope.pageSize = 10;

    $http.get('/Deals/GetDeals').success(function (data) {
        $scope.destinations = data;
        $scope.noOfPages = data.length / 10;
        $scope.maxSize = 5;
    });
});

destApp.filter('startFrom', function () {
    return function (input, start) {
        start = +start; //parse to int
        return input.slice(start);
    };
});
Episodic answered 8/9, 2013 at 2:17 Comment(0)
W
12

Because your pagination is a combination of chained filters, Angular has no idea that when cityName changes, it should reset currentPage to 1. You'll need to handle that yourself with your own $watch.

You'll also want to adjust your startFrom filter to say (currentPage - 1) * pageSize, otherwise, you always start at page 2.

Once you get that going, you'll notice that your pagination is not accurate, because it's still based on destination.length, and not the filtered sub-set of destinations. For that, you're going to need to move your filtering logic from your view to your controller like so:

http://jsfiddle.net/jNYfd/

HTML

<div data-ng-app="dealsPage">
    <input type="text" data-ng-model="cityName" />
    <div data-ng-controller="DestinationController">
        <ul>
            <li data-ng-repeat="deals in filteredDestinations | startFrom:(currentPage - 1)*pageSize | limitTo:pageSize">{{deals.Location}}</li>
        </ul>
        <br />
        <pagination rotate="true" num-pages="noOfPages" current-page="currentPage" max-size="maxSize" class="pagination-small" boundary-links="true"></pagination>
    </div>

JavaScript

var destApp = angular.module('dealsPage', ['ui.bootstrap']);

destApp.controller('DestinationController', function ($scope, $http, $filter) {
    $scope.destinations = [];
    $scope.filteredDestinations = [];

    for (var i = 0; i < 1000; i += 1) {
        $scope.destinations.push({
            Location: 'city ' + (i + 1)
        });
    }

    $scope.pageSize = 10;
    $scope.maxSize = 5;

    $scope.$watch('cityName', function (newCityName) {
        $scope.currentPage = 1;
        $scope.filteredDestinations = $filter('filter')($scope.destinations, $scope.cityName);
        $scope.noOfPages = $scope.filteredDestinations.length / 10;
    });
});

destApp.filter('startFrom', function () {
    return function (input, start) {
        start = +start; //parse to int
        return input.slice(start);
    };
});
Whydah answered 8/9, 2013 at 3:39 Comment(3)
Thanks very much! This worked well. :) All I had to do was to add this line of code to your solution: $scope.noOfPages = isNaN($scope.filteredDestinations.length / 10) ? 200 : $scope.filteredDestinations.length / 10; So that on page start, the booststrap UI pagination control would render, otherwise it wouldn't render due to the $scope.noOfPages variable having an 'NaN' value.Episodic
Awesome, happy to have helped. Welcome to StackOverflow, btw. You should considering accepting the answer if it answered your question. :)Whydah
Also, could you please help me with a similar issue as I'm stuck on this one now. I've posted it in another page, here's the link: #18722746 Thanks very much again.Episodic
I
4

The version shared on jsfiddle is compatible with ui-bootstrap 0.5.0 but from 0.6.0 onwards there have been breaking changes.

Here is a version that uses the following libraries:

  • angular 1.2.11
  • angular-ui-bootstrap 0.10.0
  • bootstrap 3.1.0

Here is a plunker for the same:

Angular UI Bootstrap Pagination

Infirm answered 4/2, 2014 at 12:35 Comment(2)
Your plunker relies on pulling js from github, which request is denied. Better option is cdnjs. plnkr.co/edit/1TfUI2DFejQ2ULamfwu9?p=previewSpearhead
Also, I believe the api for the bootstrap pagination now has ng-model rather than the page attribute the plunkr uses. angular-ui.github.io/bootstrap/#/paginationSpearhead
R
0

Hello I tried to hook this up with Firebase using Angular Fire and it only works after I type something in the search input. In the $scope.$watch method, I used Angular Fire's orderByPriorityFilter to convert the object to an array.

$scope.$watch('search', function(oldTerm, newTerm) {
  $scope.page = 1;
  // Use orderByPriorityFilter to convert Firebase Object into Array
  $scope.filtered = filterFilter(orderByPriorityFilter($scope.contacts), $scope.search);
  $scope.lastSearch.search = oldTerm;
  $scope.contactsCount = $scope.filtered.length;
});

Initial load doesn't load any contacts. It's only after I start typing in the input search field.

Rapturous answered 30/3, 2014 at 5:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.