1

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>
Pedro Mora
  • 95
  • 9

4 Answers4

1

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
li0n_za
  • 455
  • 2
  • 15
  • 1
    *"Your limitTo is wrong"*. Well, It isn't wrong, it just doesn't work as he expecting.. but it's a valid use of `limitTo`. – developer033 Aug 04 '16 at 12:47
  • @PedroMora, 'logically the limit must not affect the filter' this is the case, the issue you are having is that you are limiting to 5 from index 5. This means, if you search for 'girl' you get six results, but you are limiting to five from index 5, hence it is only showing one result – li0n_za Aug 04 '16 at 13:16
  • Then, do I have to create my own decorator???, man, I was comfortable with angular till I got this part. This is the default behavior I've seen in major datatables frameworks – Pedro Mora Aug 04 '16 at 13:17
  • no, use `limitTo : 5 track by $index` or `limitTo : 5 : 0 track by $index`, this will filter your list, and limit the results from index 0 - 5 – li0n_za Aug 04 '16 at 13:19
  • @PedroMora, since you're just starting is always good to read the docs. https://docs.angularjs.org/api/ng/filter/limitTo – developer033 Aug 04 '16 at 13:21
  • I know, and I read the docs but, I imagined this behavior like a virtual modifier, I think you understand. Another way to see it, think in events philosophy, you can hook many events as you want (In most of frameworks). Either gathering all events or overriding some, you can get a desired behavior. I thought this was th philosophy, but now I see I'm wrong. – Pedro Mora Aug 04 '16 at 13:33
0

I have found result using this

ng-repeat="friend in friends | filter:q | limitTo : 5 as results track by $index"
  • Please, set yourself in context of paging behavior of datatable, no matter the page you are, if you filter the datatable it returns matched items. – Pedro Mora Aug 04 '16 at 13:41
0

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>
Pedro Mora
  • 95
  • 9
0

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>

  • Still don't work, if you remove `$scope.q = "john"` from controller and then filter data, the issue is same as mine. – Pedro Mora Aug 05 '16 at 13:01