AngularJS – filter for undefined properties in ng-repeat?

All we need is an easy explanation of the problem, so here it is.

For my AngularJS project (v1.2.3), I have a list of routes and am trying to build a navigation bar from the object. What I want to do is display any object with an undefined isRight property in one style, and where the property is defined in another.

In one ng-repeat I would like to filter those objects with an undefined isRight property. How can I accomplish this inside the ng-repeat attribute, without having to resort to creating a custom filter function?

$scope.nav = [
    { path: '/', title: 'Home' },
    { path: '/blog', title: 'Blog' },
    { path: '/about', title: 'About' },
    { path: '/login', title: 'Login', isRight: true }
];

I realize I could just add the attribute isRight: false to each object, or have separate nav objects for right and left side links, and other such simple workarounds, but I am curious if there is a way to accomplish this with the current structure, using something along the lines of:

<li ng-repeat="link in nav | filter:{isRight:undefined}">

This is more a curiosity than a need, but I appreciate any suggestions.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

You can negate a filter expression. So instead of dealing with undefined you can just filter out anything where isRight is not (!) true. Like this:

<li ng-repeat="link in nav | filter:{isRight:'!true'} ">

And for the opposite you can, of course, do:

<li ng-repeat="link in nav | filter:{isRight:'true'} ">

demo fiddle

Method 2

You can also use
<li ng-repeat="link in nav | filter:{isRight:'!'}">

see also How to display placeholders in AngularJS for undefined expression values?

var app = angular.module('myApp', [])

app.controller('mainCtrl',['$scope' , function($scope) {


$scope.nav = [
    { path: '/', title: 'Home' },
    { path: '/blog', title: 'Blog' },
    { path: '/about', title: 'About' },
    { path: '/login', title: 'Login', isRight: true }
];
  
}])
<div ng-app="myApp" ng-controller="mainCtrl">

<h3>!isRight</h3>
<ul>
<li ng-repeat="link in nav | filter:{isRight:'!'}">{{link.title}}</li>
</ul>
  
  
<h3>isRight</h3>
<ul>
<li ng-repeat="link in nav | filter:{isRight:'!!'}">{{link.title}}</li>
</ul>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

Method 3

Edit

I just reread the question, and this is not the answer you are looking for. I will leave it here for documentation purposes, but I do not think there is a way to get the functionality you desire without either building a custom filter or using a custom filter function.

To expand on why looking for undefined will not work with the default filter we take a look at this code from the AngularJS filter implementation.

switch (typeof expression) {
  ...
  case "object":
    // jshint +W086
    for (var key in expression) {
      (function(path) {
        if (typeof expression[path] == 'undefined') return;
        predicates.push(function(value) {
          return search(path == '$' ? value : getter(value, path), expression[path]);
        });
      })(key);
    }
    break;
  ...
}

If a value for the filter object’s property is undefined, no predicate function is added to the predicates array. In your case you are setting the value of the isRight property to undefined (your filter expression is {isRight:undefined}).

Original Answer

You can always use a predicate function to create arbitrary filters (documentation).

A predicate function can be used to write arbitrary filters. The
function is called for each element of array. The final result is an
array of those elements that the predicate returned true for.

In your controller create a method to pick the items you want

$scope.isRightUndefined = function(item) {
  return item.isRight === undefined;
}

and change your repeat to be

<li ng-repeat="link in nav | filter:isRightUndefined">

Method 4

module.filter('notNullOrUndefined', [function () {
    return function (items, property) {
        var arrayToReturn = [];
        for (var i = 0; i < items.length; i++) {
            var test = property !== undefined ? items[i][property] : items[i];
            if (test !== undefined && test !== null) {
                arrayToReturn.push(items[i]);
            }
        }
        return arrayToReturn;
    };
}]);

Usage:

<div class="col-md-12" ng-repeat="style in styles | notNullOrUndefined:'background'">
    <span class="ed-item">{{style.name}} Background</span>
</div>

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply