2

I tried to create a textbox based filtering of a ng-repeat result. Though there is no errors listed, the filtering is not working. What is missing here?

Updated code after making following change:

<tbody ng-repeat="objReview in reviewsList | myCustomFilter:criteria" > 

The filter is gettiing called two times for each row. How to make it call only once?

Code

<html>
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-resource.js"></script>
    <style type="text/CSS">
        table
        {border-collapse: collapse;width: 100%;}
        th, td
        {text-align: left;padding: 8px;}
        tr:nth-child(even)
        {background-color: #f2f2f2;}
        th
        {background-color: #4CAF50;color: white;}
    </style>
    <script type="text/javascript">
        //defining module
        var app = angular.module('myApp', ['ngResource']);

        //defining factory
        app.factory('reviewsFactory', function ($resource) {
            return $resource('https://api.mlab.com/api/1/databases/humanresource/collections/Reviews',
                    { apiKey: 'myKey' }
                  );
        });

        //defining controller
        app.controller('myController', function ($scope, reviewsFactory) 
    {


         $scope.criteria = "";
     $scope.reviewsList = reviewsFactory.query();

        });

    app.filter('myCustomFilter', function () 
    {

        return function (input, criteria) 
        {

        var output = [];

            if (!criteria || criteria==="") 
        {

            output = input;
            }
            else 
        {
                  angular.forEach(input, function (item) 
          {
                    alert(item.name);
            //alert(item.name.indexOf(criteria));

            //If name starts with the criteria
            if (item.name.indexOf(criteria) == 0) 
            {
                            output.push(item)
                    }
                  });
            }
            return output;
        }
    });

    </script>
</head>
<body ng-app="myApp">
    <div ng-controller="myController">

    <label>
        SEARCH FOR: <input type="text" ng-model="criteria">
    </label>
        <table>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Review Date</th>
                </tr>
            </thead>
            <tbody ng-repeat="objReview in reviewsList | myCustomFilter:criteria" >
                <tr>
                    <td>{{objReview.name}}</td>
                    <td>{{objReview.createdOnDate}}</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

Further Reading

  1. Is this normal for AngularJs filtering
  2. How does data binding work in AngularJS?
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
  • 2
    You don't need to specify `filter`, just `myCustomFilter:criteria`. – Lex Jun 19 '16 at 03:46
  • @Lex I updated the question. Now the filter is working - but fired the code two times for each row. Is it expected behavior? Or is there anything to be fixed ? – LCJ Jun 20 '16 at 01:04
  • 1
    That is how the digest cycle in Angular works. – Lex Jun 20 '16 at 02:23

1 Answers1

2

As noted in the comments by Lex you just need to get rid of the prefix 'filter', so change

<tbody ng-repeat="objReview in reviewsList | filter:myCustomFilter:criteria" >

to

<tbody ng-repeat="objReview in reviewsList | myCustomFilter:criteria" >  

In addition you should set an initial value for your controller's property criteria as otherwise your initial list will be empty as your filter will not match anything due to the comparison operator === which takes the operands' types into account and critiera will be undefined until you first enter something in your textbox.

app.controller('myController', function ($scope, reviewsFactory) 
{
    $scope.criteria = '';
    $scope.reviewsList = reviewsFactory.data();
});
DAXaholic
  • 33,312
  • 6
  • 76
  • 74
  • 1
    Could also change the if to `if (!criteria || criteria==="")`. – Lex Jun 19 '16 at 16:52
  • 1
    Of course but I like it to init the models in the controller instead of having them created at some time during user interaction - which may not happen at all which would cause all code accessing that model to be aware of that fact and deal with it with additional checks. – DAXaholic Jun 19 '16 at 16:56
  • Sure, if you're using it for more than a filter criteria that makes sense. However, as soon as you rely on initializing it in the controller now everyone who uses the filter you wrote has to know to do that. I prefer to keep things self-contained as much as possible. – Lex Jun 19 '16 at 17:00
  • @DAXaholic The filter is gettiing called two times for each row. How to make it call only once? I have updated the code in the question, with alert statement. – LCJ Jun 20 '16 at 01:04
  • 1
    That question was asked on SO already and in short: you can't really (and don't need to) do anything about it. See here for that question on SO with some more links about deeper info http://stackoverflow.com/questions/20798566/angularjs-calling-filter-twice-with-ng-repeat. – DAXaholic Jun 20 '16 at 04:07