Search code examples
angularjsangular-filters

Filter for nested objects to return all children elements


I have a filter that is on ng-repeat and compares strings of all objects (including nested ones) to a search string. If the search string is found in the object, it returns true.

I'm looking for a way to extend this functionality so that when the search string matches with a string in the object, the filter will return true for that object and will return true for all nested objects in the matching object (this is a tree view, I'm searching for a node and want to show all children nodes when matched).

How would I do that?

My filter looks like this:

 .filter('deepFilter', function ($filter) {
    return function(text) {
       return function (value) {
            if(text && text.length > 0) {
              var searchTerm = text;
              if (angular.isObject(value)) {
                  var found = false;
                  angular.forEach(value, function(v) {
                      found = found || $filter('deepFilter')(searchTerm)(v);
                  });
                  return found;

              } else if (angular.isString(value)) {
                  if (value.indexOf(searchTerm) !== -1) {
                      return true;
                  } else {
                      return false;
                  }
              }
            } else {
              return true;
            }
        };
     };
   });

Solution

  • The solution I found is by using a function in the isString part of the filter, and iterating over the collection. If I find the object, I look for it's children using a recursive function and set a visibleAsAChild property for these. Then, I've added a condition in the isObject evaluation to return true for these object that have visibleAsAChild prop. I'm not sure if this is the most efficient way to do it, but it certainly works.

    .filter('deepFilter', function ($filter) {
    var currentObject;
    var setChildrenToVisible = function(node) {
      angular.forEach(node.nodes, function(node) {
        if(node.nodes) {
          setChildrenToVisible(node);
        }
        node.visibleAsAChild = true;
      });
    };
    var lookupChildren = function(o, value) {
      // console.log(o);
      angular.forEach(o.nodes, function(node) {
        if (node.name === value) {
          setChildrenToVisible(node);
        }
      });
    };
    
    return function(text) {
    
       return function (value) {
    
            if(text && text.length > 0) {
              var searchTerm = text;
              if (angular.isObject(value)) {
                  var found = false;
                  angular.forEach(value, function(v) {
                    found = found || $filter('deepFilter')(searchTerm)(v);
                  });
                  if(found && value.hasOwnProperty('id')) {
                    currentObject = value;
                  }
                  if(value.hasOwnProperty('id') && value.visibleAsAChild) {
                    return true;
                  }
                  return found;
    
              } else if (angular.isString(value)) {
                  if (value.indexOf(searchTerm) !== -1) {
                    if(currentObject){
                      lookupChildren(currentObject, value);
                    }
                      return true;
                  } else {
                      return false;
                  }
              }
            } else {
              return true;
            }
        };
     };