Search code examples
angularjsangular-filters

How to write a filter to search nested json in angular?


I wrote a little program in angular using ui-select. And I want to write a filter that do an OR search in different fields (that can be nested fields in a json). In the github of ui-select I found this filter that can do a similar things (but only with simple fields) :

/**
 * AngularJS default filter with the following expression:
 * "person in people | filter: {name: $select.search, age: $select.search}"
 * performs an AND between 'name: $select.search' and 'age: $select.search'.
 * We want to perform an OR.
 */
app.filter('propsFilter', function() {
  return function(items, props) {
    var out = [];

    if (angular.isArray(items)) {
      var keys = Object.keys(props);

      items.forEach(function(item) {
        var itemMatches = false;

        for (var i = 0; i < keys.length; i++) {
          var prop = keys[i];
          var text = props[prop].toLowerCase();
          if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
            itemMatches = true;
            break;
          }
        }

        if (itemMatches) {
          out.push(item);
        }
      });
    } else {
      // Let the output be the input untouched
      out = items;
    }

    return out;
  };
});

My json object where I want to apply this filter has this structure :

$scope.contracts = [{
  name: "contract1.00",
  value: 10,
  id :{
    id : 8000,
    code : 2
  },
  policy : {
    info : {
      name : "test1",
      country : "test"
    }
  }
}
//other elements....

The problem is that this 'propsFilter' only works with simple fields. So, if I write this :

 propsFilter: {name: $select.search, value : $select.search}

It will work correcly and do an OR search in these two fields (name OR values). But in my example, I want to do the OR search with two others fields : id.id and policy.info.name.

So, what I want to do is to use a line like this :

propsFilter: {name: $select.search, value : $select.search, id.id : $select.search, policy.info.name : $select.search }

Finally, here is my plunker : http://plnkr.co/edit/ej2r7XqeTPOC5d1NDXJn?p=preview

How can I do that in the same search filter??


Solution

  • I've updated your plunker. First of all, break from the loop caused the filter to work only on first property name, I've also added a condition in the if item[prop] && so your code doesn't throw an error when the property does not exist on the item

    http://plnkr.co/edit/CwNANzodvjnuMCNyJYtA?p=preview

    app.filter('propsFilter', function($parse) {
      return function(items, props) {
        var out = [];
    
        if (angular.isArray(items)) {
          var keys = Object.keys(props);
    
          items.forEach(function(item) {
            var itemMatches = false;
    
            for (var i = 0; i < keys.length; i++) {
              var prop = $parse(keys[i])(item);
              var text = props[keys[i]].toLowerCase();
    
              if (prop && prop.toString().toLowerCase().indexOf(text) !== -1) {
                itemMatches = true;
              }
            }
    
            if (itemMatches) {
              out.push(item);
            }
          });
        } else {
          // Let the output be the input untouched
          out = items;
        }
    
        return out;
      };
    });