Angular filter and limitTo not working as expected

1.8k Views Asked by At

I'm a newbie in angular.js and I'm seeing something I don't like. When I filter and limit the data at the same time, seems like the limit affects the filter and it can't search back in the original array source. Am I missing something?

UPDATE: Thing is I'm trying to create a simple data table directive with pagination, filter and some other things, but this is driving me crazy. I've seen the answers and comments and I'm wondering if the procedure that I'm trying to reach is wrong, but no, logically the limit must no affect the filter (I think so), because maybe in some time you'll need to search back in the whole source, not the limmitted part, they should be separated behaviors. Isn't this the default behavior I've seen in major datatables frameworks?

Here is an example describing the issue:

angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
        $scope.q="john"
        $scope.friends = [
          {name:'John', age:25, gender:'boy'},
          {name:'Jessie', age:30, gender:'girl'},
          {name:'Johanna', age:28, gender:'girl'},
          {name:'Joy', age:15, gender:'girl'},
          {name:'Mary', age:28, gender:'girl'},
          {name:'Peter', age:95, gender:'boy'},
          {name:'Sebastian', age:50, gender:'boy'},
          {name:'Erika', age:27, gender:'girl'},
          {name:'Patrick', age:40, gender:'boy'},
          {name:'Samantha', age:60, gender:'girl'}
        ];
      });
.example-animate-container {
        background:white;
        border:1px solid black;
        list-style:none;
        margin:0;
        padding:0 10px;
      }

      .animate-repeat {
        line-height:30px;
        list-style:none;
        box-sizing:border-box;
      }

      .animate-repeat.ng-move,
      .animate-repeat.ng-enter,
      .animate-repeat.ng-leave {
        transition:all linear 0.5s;
      }

      .animate-repeat.ng-leave.ng-leave-active,
      .animate-repeat.ng-move,
      .animate-repeat.ng-enter {
        opacity:0;
        max-height:0;
      }

      .animate-repeat.ng-leave,
      .animate-repeat.ng-move.ng-move-active,
      .animate-repeat.ng-enter.ng-enter-active {
        opacity:1;
        max-height:30px;
      }
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.min.js"></script>
<div ng-app="ngRepeat" ng-controller="repeatController">
The flollowing example shows when you filter and limit the data at the same time, you can not filter the items before the current position.
<br/>
<b>position</b> : 5<br/>
<b>limitTo</b> : 5<br/>
<b>filter</b> : { '$' : '{{q}}' }<br/>
        I have {{friends.length}} friends. They are:
        <input type="search" value="john" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
        <ul class="example-animate-container">
          <li class="animate-repeat" ng-repeat="friend in friends | filter:q | limitTo : 5 : 5 as result track by $index">
            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
          </li>
          <li class="animate-repeat" ng-if="results.length == 0">
            <strong>No results found...</strong>
          </li>
        </ul>
<br/>As you can see there's no results.
      </div>

4

There are 4 best solutions below

0
Pedro Mora On BEST ANSWER

Following DataTables.net philosophy, when you start filter data, it goes back to the first page. I hope it helps others

angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
        $scope.position=5;
        $scope.resetPagination = function(){
          if(this.position !== 0) this.position = 0;
        }
        $scope.friends = [
          {name:'John', age:25, gender:'boy'},
          {name:'Jessie', age:30, gender:'girl'},
          {name:'Johanna', age:28, gender:'girl'},
          {name:'Joy', age:15, gender:'girl'},
          {name:'Mary', age:28, gender:'girl'},
          {name:'Peter', age:95, gender:'boy'},
          {name:'Sebastian', age:50, gender:'boy'},
          {name:'Erika', age:27, gender:'girl'},
          {name:'Patrick', age:40, gender:'boy'},
          {name:'Samantha', age:60, gender:'girl'}
        ];
      });
.example-animate-container {
        background:white;
        border:1px solid black;
        list-style:none;
        margin:0;
        padding:0 10px;
      }

      .animate-repeat {
        line-height:30px;
        list-style:none;
        box-sizing:border-box;
      }

      .animate-repeat.ng-move,
      .animate-repeat.ng-enter,
      .animate-repeat.ng-leave {
        transition:all linear 0.5s;
      }

      .animate-repeat.ng-leave.ng-leave-active,
      .animate-repeat.ng-move,
      .animate-repeat.ng-enter {
        opacity:0;
        max-height:0;
      }

      .animate-repeat.ng-leave,
      .animate-repeat.ng-move.ng-move-active,
      .animate-repeat.ng-enter.ng-enter-active {
        opacity:1;
        max-height:30px;
      }
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.min.js"></script>
<div ng-app="ngRepeat" ng-controller="repeatController">
The flollowing example shows when you filter and limit the data at the same time, you can not filter the items before the current position.
<br/>
<b>limitTo</b> : 5<br/>
<b>position</b> : {{position}}<br/>
<b>filter</b> : { '$' : '{{q}}' }<br/>
        I have {{friends.length}} friends. They are:
        <input type="search" value="john" ng-model="q" placeholder="filter friends..." aria-label="filter friends" ng-change="resetPagination()"/>
        <ul class="example-animate-container">
          <li class="animate-repeat" ng-repeat="friend in friends | filter:q | limitTo : 5 : position as results track by $index">
            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
          </li>
          <li class="animate-repeat" ng-if="results.length == 0">
            <strong>No results found...</strong>
          </li>
        </ul>
<br/>As you can see there's results.
      </div>

6
li0n_za On

You are having issues because your limitTo is limiting 5 items, beginning from index 5, when you search there will most likely not be 5 items, especially not with the data provided.

Change

limitTo : 5 : 5 track by $index

to

limitTo : 5 track by $index

or

limitTo : 5 : 0 track by $index // 0 = Index at which to begin limitation
1
Tarek Salah uddin Mahmud On

I have found result using this

ng-repeat="friend in friends | filter:q | limitTo : 5 as results track by $index"
1
Tarek Salah uddin Mahmud On

This may help you..this is with pagination

<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.min.js"></script>
<script>
    var app=angular.module('ngRepeat', ['ngAnimate']);
    app.controller('repeatController', function($scope) {
    $scope.q="john";
    $scope.currentPage = 0;
    $scope.pageSize = 5;
    $scope.numberOfPages=function(){
        return Math.ceil($scope.friends.length/$scope.pageSize);                
    }
    $scope.friends = [
      {name:'John', age:25, gender:'boy'},
      {name:'Jessie', age:30, gender:'girl'},
      {name:'Johanna', age:28, gender:'girl'},
      {name:'Joy', age:15, gender:'girl'},
      {name:'Mary', age:28, gender:'girl'},
      {name:'Peter', age:95, gender:'boy'},
      {name:'Sebastian', age:50, gender:'boy'},
      {name:'Erika', age:27, gender:'girl'},
      {name:'Patrick', age:40, gender:'boy'},
      {name:'Samantha', age:60, gender:'girl'}
    ];
  });
  app.filter('startFrom', function() {
    return function(input, start) {
        start = +start; //parse to int
        return input.slice(start);
    }
});
</script>


<style>
    .example-animate-container {
        background: white;
        border: 1px solid black;
        list-style: none;
        margin: 0;
        padding: 0 10px;
    }

    .animate-repeat {
        line-height: 30px;
        list-style: none;
        box-sizing: border-box;
    }

    .animate-repeat.ng-move,
    .animate-repeat.ng-enter,
    .animate-repeat.ng-leave {
        transition: all linear 0.5s;
    }

    .animate-repeat.ng-leave.ng-leave-active,
    .animate-repeat.ng-move,
    .animate-repeat.ng-enter {
        opacity: 0;
        max-height: 0;
    }

    .animate-repeat.ng-leave,
    .animate-repeat.ng-move.ng-move-active,
    .animate-repeat.ng-enter.ng-enter-active {
        opacity: 1;
        max-height: 30px;
    }
</style>

<div ng-app="ngRepeat" ng-controller="repeatController">
    The flollowing example shows when you filter and limit the data at the same time, you can not filter the items before the
    current position.
    <br/>
    <b>position</b> : 5<br/>
    <b>limitTo</b> : 5<br/>
    <b>filter</b> : { '$' : '{{q}}' }<br/> I have {{friends.length}} friends. They are:
    <input type="search" value="john" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
    <ul class="example-animate-container">
        <li class="animate-repeat" ng-repeat="friend in friends | filter:q | startFrom:currentPage*pageSize | limitTo:pageSize as results track by $index">
            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
        </li>
        <li class="animate-repeat" ng-if="results.length == 0">
            <strong>No results found...</strong>
        </li>
    </ul>
    <button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">
    Previous
</button>
{{currentPage+1}}/{{numberOfPages()}}
<button ng-disabled="currentPage >= friends.length/pageSize-1" ng-click="currentPage=currentPage+1;">
    Next
</button>
</div>