2

I have an array of objects in scope, which I'm looping over with ng-repeat. I want to filter out some elements based on their values, but it seems like ng-if is completely ignored.

I've set up this simple plunk to illustrate: http://plnkr.co/edit/Aj0hpZQTfnkQ6BYG8DRb.
Here's the controller:

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';

  $scope.events = [
    {key: "ClassName", value: "exception", age:12},
    {key: "Message", value: "oops", age: 25},
    {key: "Stack", value: null, age: 35}
    ]
});

And the HTML template:

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js" data-semver="1.0.8"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>
    <ul>
      <li ng-repeat="event in events" ng-if="event.age > 20">
        [{{$index + 1}}] {{event.key}}: {{event.value}} - {{event.age}}
      </li>
    </ul>
  </body>

</html>

The ng-if directive is supposed to filter out the first element of the array, bit it is displayed anyway. What have I missed that ignores the ng-if?

1
  • "The ng-if directive is supposed to filter", no its not, it suppose to decide whether the whole element will be rendered or not, voted for CD.. Commented Nov 12, 2013 at 8:50

3 Answers 3

4

I suppose the right way would be using a filter, for example:

<li ng-repeat="event in events | filter:greaterThan">

Working example: http://plnkr.co/edit/Qy3BVEogEgGivtqel73b?p=preview

Sign up to request clarification or add additional context in comments.

2 Comments

I didn't know that function call syntax. Thx!
How about creating a reusable custom filter instead of attaching a function to the scope that compares hardcoded values, item.age and 20. Take a look at this: plnkr.co/edit/h8Hm6JKm9WOZLHSMnc8R?p=preview
1

I've updated your Plunkr to solve your issue using the code below.

I frequently use two custom angular modules, FilterIncludeIf and UtilCompare to provide ng-if-like functionality to ng-repeats.

FilterIncludeIf has UtilCompare as a dependency and takes two arguments. The first is the comparison operator in string form (">=" in your case).

The second is an Object. The keys should be set to the keys of the individual objects returned from the ng-repeat statement (customer in the example below) whose values you'd like to compare. The values should be set to the value you'd like to compare against.

Looking at example-snippet.html, includeIf ends up filtering customers.all and ensuring that the only customers that are rendered are ones where all of the following statements are true.

customer[firstname] != customers.all.current.data.firstname`
customer[lastname] != customers.all.current.data.lastname`
customer[email] != customers.all.current.data.email`

example-snippet.html

<li 
    ng-repeat="customer in customers.all | filter:query | includeIf : '!=' : {
        firstname   : customers.current.data.firstname, 
        lastname    : customers.current.data.lastname,
        email       : customers.current.data.email, 
    }" 
    class="tab"
    >
    <div class="info">
        <h4 class="subheader name">
            {{customer.firstname}}&nbsp;{{customer.lastname}}
        </h4>
        {{customer.email}}
    </div>
</li>

Filter - includeIf

angular
.module('FilterIncludeIf', [
    'UtilCompare'
])  
.filter('includeIf', function (compare) {
    return function (items, comparator, attrs) {
        var filteredInput = [];

        for (var i = 0; i < items.length; i++) {
            var item = items[i],
                numOfAttrs = 0,
                numOfMatchedAttrs = 0;
            for (var key in attrs) {
                numOfAttrs++
                var value = attrs[key],
                    comparison = compare
                        .these(item[key], value)
                        .by(comparator)
                        .eval();

                if (comparison) numOfMatchedAttrs++;
            }
            if (numOfAttrs == numOfMatchedAttrs) filteredInput.push(item);
        }

        return filteredInput
    };
});

Factory - compare

angular
.module('UtilCompare', [])
.factory('compare', function () {
    var _by = undefined,
        _a = undefined,
        _b = undefined;

    var comparators = {
        '>'     : function(c, d) { return c > d },
        '<'     : function(c, d) { return c < d },
        '>='    : function(c, d) { return c >= d },
        '<='    : function(c, d) { return c <= d },
        '!='    : function(c, d) { return c != d },
        '!=='   : function(c, d) { return c !== d },
        '=='    : function(c, d) { return c == d },
        '==='   : function(c, d) { return c === d },
    }

    function by (comparator) {
        _by = comparator;
        return this
    }

    function these (a, b) {
        _a = a;
        _b = b;
        return this
    }

    function eval () {
        return comparators[_by](_a, _b);
    }

    return {
        by     : by,
        these  : these,
        eval   : eval
    }

});

Comments

0

I think what you want is an actual filter.

Filters can be applied on expressions (like the repeat) and will return a list containing only items that fulfills the criteria of the filter. So in this case I think you would do.

  <li ng-repeat="event in events | maxAge:20">
    ...
  </li>

Note that maxAge is not one of the default filters (you can find them to the left in the here), you would have to create one on your own unless you can fit it into the existing ones. But creating filters is really easy, they are just functions that take an input, do whatever logic they feel like and then return the new values.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.