AngularJS: get filtered ng-repeat result count to update my pagination

My code explained:

Below is a simple table with an ng-repeat that loops through ctrl.persons, and stores the results in the variable ctrl.results. There is a filter included that allows a user to search the results using a text input, and sliceResults is a custom filter for my Angular-UI pagination (it determines the offset and limit of results to display based on the current page of the pagination).

<table>
    <tr ng-repeat="result in ctrl.results = (ctrl.persons | filter:searchKeywords) | sliceResults: ctrl.paginationOffset : ctrl.paginationLimit">
        <td>{{ $index+1 }}</td>
        <td>{{ result.firstName }}</td>
        <td>{{ result.lastName }}</td>
    </tr>
</table>

Results : {{ ctrl.results.length }}

Situation:

At the end of the code, ctrl.results.length returns the amount of results real-time, and changes accordingly when a user fills out the text input to search through the results.

However, when the amount of results change, my pagination does not change accordingly and therefore shows an incorrect amount of pages (e.g. it can still show 3 pages while there is only 1 page). The amount of pages is based on a variable in my controller (this.paginationTotalItems).

Question:

How do I update the variable this.paginationTotalItems in my controller as soon as the amount of results (ctrl.results.length) change in my view? (Or, what alternative way is there to update my pagination?)

What I tried:

I am far from being an AngularJS expert, so don't laugh at me! I tried to put a function in ng-change with the result count, which failed because apparently it requires ng-model and I don't see that working with ng-repeat.

I also tried to use $watch on the ctrl.results variable, but that results in a massive console error because it aborts after x iterations. Also, I'd rather not use $watch because I read it only works with $scope, which I'd rather not use (I use the "controller as" syntax).

Thanks in advance, I'll accept the answer that helps me solve my issue.

Answers:

Answer

You can use a $watch with a controllerAs syntax in the controller. I know you said it's not your priority, but I think it might be your best solution. It will look something like this:

.controller('myCtrl', function () {
    var ctrl = this;
    ...
    $scope.$watch(angular.bind(this, function () {
      return this.results.length;
    }), function (newVal) {
        ctrl.paginationTotalItems = //whatever logic you have
    });
    ....
});

Edit: Ok so I've just thought of another way that you don't have to use $scope. It includes a function on your controller, and calling it using ng-init which will update your variable (ng-init will fire everytime the iterated list changes):

So after much simplification, your controller will look like this:

function MyCtrl() {
    var ctrl = this;
    ctrl.paginationTest = 1;
    ctrl.persons = [1,2,3,4,5];

    ctrl.calcPagination = function () {
        ctrl.paginationTest = Math.floor(Math.random() * 10);
    }
}

And the HTML will be:

<table>
          <input ng-model="searchKeywords"/>
        <tr ng-repeat="result in ctrl.results = (ctrl.persons | filter:searchKeywords)" ng-init="ctrl.calcPagination()">

            <td>{{ result }}</td>
            <td>{{ctrl.paginationTest}}</td>
        </tr>
    </table>

Here's a Fiddle, take a look at the 2nd column, and see that it changes after every change in the input element. In your case you'll just recalculate ctrl.paginationTotalItems.

Answer

I know this question is answered, but I want to share my experience.

I was having some issues with the use of ng-init proposed by @Omri Aharon because it was not calling function each time I changed the value in the text input, I solved it just removing ng-init from <tr> and adding ng-change="ctrl.calcPagination()" to the input.

The HTML code is like:

<tbody>
  <div class="input-group">
    <span class="input-group-addon glyphicon glyphicon-search" aria-hidden="true"></span>
    <input type="text" class="form-control" placeholder="Filter..." ng-model="ctrl.filterName" ng-change="ctrl.calcPagination()">
  </div>
  <tr ng-repeat="item in ctrl.list | filter: ctrl.filterName | orderBy: ctrl.sortType:ctrl.sortReverse | clientPaginator:ctrl.currentPage:ctrl.itemsPerPage">
    <td>{{item.property}}</td>
  </tr>
</tbody>

And the function in the controller:

ctrl.calcPagination = function(){
    ctrl.resultCount =  $filter('filter')(ctrl.list, ctrl.filterName).length;
}

Thanks @Omri Aharon for this simple solution!

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.