Angular-tree-component: treeModel.filterNodes not expanding children elements of searching node

Created on 8 May 2018  路  12Comments  路  Source: CirclonGroup/angular-tree-component

Hi. Like on official demo on filtering nodes. When you trying to filter by subcategory name you cant expand this category to see all childrens. Is it possible to fix?

Link: https://angular2-tree.readme.io/docs/filtering

Most helpful comment

I came up with a better and fast solution by custom filter function to this problem-

(keyup.enter)="filterNodes=nodes; filterTree(filter.value, tree.treeModel)"

filterTree(value: string, treeModel: TreeModel) {
if (value) {
this.temp = [];
for (let i = 0; i < this.nodes.length; i++) {
this.showChildren(this.nodes[i], this.nodes[i], value, treeModel);
}
this.filterNodes = this.temp;
} else {
this.filterNodes = this.nodes;
}
}

showChildren(node, item, value, treeModel) {
const nodeName = item.name.toLowerCase();
const searchValue = value.toLowerCase();
const nodeFound = nodeName.includes(searchValue);

if (nodeFound) {
 setTimeout(() => {
    const someNode = treeModel.getNodeById(item.id);
    if (someNode) { someNode.ensureVisible(); }
  });
  this.temp.push(node);
} else {
  if (item.children && item.children.length) {
    for (let i = 0; i < item.children.length; i++) {
      this.showChildren(node, item.children[i], value, treeModel);
    }
  }
}

}

All 12 comments

Hi, please paste link to plnkr to reproduce. Thanks

I got the same issues here:
I have a tree like this:

            __ child_1.1
            __child_1.2
           __child_.13
parent_2
          __child 2.1

Here is my search function

filterFn(value, treeModel: TreeModel) {
        treeModel.filterNodes((node) => fuzzysearch(value, node.data.fullName));
    }

fuzzy search

function fuzzysearch(text, list) {
    const listLC = list.toLowerCase();
    const textLC = text.toLowerCase();
    const listLength = list.length;
    const textLength = textLC.length;

    if (textLength > listLength) {
        return false;
    }
    if (textLength === listLength) {
        return textLC === listLC;
    }
    outer: for (let i = 0, j = 0; i < textLength; i++) {
        const characterOfText = textLC.charCodeAt(i);
        while (j < listLength) {
            if (listLC.charCodeAt(j++) === characterOfText) {
                continue outer;
            }
        }
        return false;
    }
    return true;
}

If I search parent_1 then I only get parent_1 and cannot expand children element inside parent_1. It means that this filter function only return the data which match with the key word that I input.
Btw, if I search __ child_1.1, it shows parent_1 and I can expand the children but it also only shows __ child_1.1 element .

I wonder if is it possible to fix ?
Thanks.

Hi,
the filtering will not show children of parents if the children themselves don't answer the search term.
If you need this kind of behaviour you will need to implement it yourself and set the hidden nodes by either passing in a state object with hiddenNodesIds, or calling hide/show/setIsHidden methods on the treeNode.

All the best

I've been able to display the children of filter matches by adding a data attribute to every node with the full path as a string:

ngAfterViewInit() {
    this.FilterTree.treeModel.doForAll((node: TreeNode) => node.data['path_string'] = this.pathString(node, node.data.name+", "));
  }
pathString(node: TreeNode, parentString: string) {
    if (node.parent.data.name === undefined) {
      return parentString.slice(0, -2);
    }
    else {
      return this.pathString(node.parent, parentString + node.parent.data.name + ", ");
    }
  }

This can then be parsed by your filter function, with an additional function (expandExactMatches) to ensure any nodes which contain the input string are visible:

stringFilter(value: string, treeModel: TreeModel) {
    treeModel.filterNodes((node: TreeNode) => this.parentStringSearch(value, node),false);
    this.expandExactMatches(value,treeModel)
  }
expandExactMatches(value: string, treeModel: TreeModel){
    treeModel.collapseAll();
    treeModel.doForAll(function(node: TreeNode){
      if(node.data.name.toLowerCase().indexOf(value.toLowerCase()) >= 0){
        console.log(node);
        node.ensureVisible();
      }
    });
  };

To maintain performance it may be necessary to call the filter function only after a minimum number of characters has been input:

<input id="filter" #filter (keyup)="filter.value.length > 3 && stringFilter(filter.value, tree.treeModel)" placeholder="filter nodes" />

@ClientsideDesign
Can you share code of this function: this.parentStringSearch
treeModel.filterNodes((node: TreeNode) => this.parentStringSearch(value, node),false);
Thanks.

Sure:

  parentStringSearch(needle: string, node: TreeNode){
    if (node.data.path_string.toLowerCase().indexOf(needle.toLowerCase()) >= 0) {
      return true;
    }
    else {
      return false;
    }
  }

@ClientsideDesign Hi, I am getting that issue, Could you please share your code on Github or make a demo on https://jsfiddle.net/ for me.
I tried many time as your example above but still not work.
Thank for advance!

I came up with a simple solution to this problem. You check if the matching node has the children. If it has, then store it to an array. After the filtering is done, you will loop the array of found nodes with children. In that loop, you will loop for all the children and use show() and ensureVisible() on them

  filterTree(value: string, treeModel: TreeModel) {
    let foundResults: TreeNode[] = [];
    treeModel.filterNodes((node: TreeNode) => {
      const nodeName = node.data.name.toLowerCase();
      const searchValue = value.toLowerCase();
      const nodeFound = nodeName.includes(searchValue);
      if (nodeFound && node.hasChildren) {
        foundResults.push(node);
      }
      return nodeFound;
    });
    foundResults.forEach((item) => {
      item.children.forEach((child) => {
        child.show();
        child.ensureVisible();
      });
    });
  }

If the children also have children u can change the code of @dinohorvat like the following:
` filterTree(value: string, treeModel: TreeModel) {

let foundResults: TreeNode[] = [];
treeModel.filterNodes((node: TreeNode) => {
  const nodeName = node.data.name.toLowerCase();
  const searchValue = value.toLowerCase();
  const nodeFound = nodeName.includes(searchValue);
  if (nodeFound && node.hasChildren) {
    console.log(node.children)
    for (let child of node.children) {
      if(child.hasChildren){
        foundResults.push(child);
      }
    }
    foundResults.push(node);

  }
  return nodeFound;
});
foundResults.forEach((item) => {
  item.children.forEach((child) => {
    child.show();
    child.ensureVisible();
  });
});

}`

To ensure multi level of children to be visible. use this function.

foundResults.forEach((item) => {
  this.showChildren(item);
});

static showChildren(node) {
    if (node.children && node.children.length) {
        node.children.forEach((child) => {
            this.showChildren(child);
        });
    }
}

I came up with a simple solution to this problem. You check if the matching node has the children. If it has, then store it to an array. After the filtering is done, you will loop the array of found nodes with children. In that loop, you will loop for all the children and use show() and ensureVisible() on them

  filterTree(value: string, treeModel: TreeModel) {
    let foundResults: TreeNode[] = [];
    treeModel.filterNodes((node: TreeNode) => {
      const nodeName = node.data.name.toLowerCase();
      const searchValue = value.toLowerCase();
      const nodeFound = nodeName.includes(searchValue);
      if (nodeFound && node.hasChildren) {
        foundResults.push(node);
      }
      return nodeFound;
    });
    foundResults.forEach((item) => {
      item.children.forEach((child) => {
        child.show();
        child.ensureVisible();
      });
    });
  }

Hi, I tried this, it takes a while when you have a lot of nodes. In order to make it more usable, we tried to use a loader template that show whenever we execute a search on the tree. However, we notice that use of .show .ensureVisible somehow blocks the update of the template that's using this componente. Do you have any recommendations on how to troubleshoot this?

I came up with a better and fast solution by custom filter function to this problem-

(keyup.enter)="filterNodes=nodes; filterTree(filter.value, tree.treeModel)"

filterTree(value: string, treeModel: TreeModel) {
if (value) {
this.temp = [];
for (let i = 0; i < this.nodes.length; i++) {
this.showChildren(this.nodes[i], this.nodes[i], value, treeModel);
}
this.filterNodes = this.temp;
} else {
this.filterNodes = this.nodes;
}
}

showChildren(node, item, value, treeModel) {
const nodeName = item.name.toLowerCase();
const searchValue = value.toLowerCase();
const nodeFound = nodeName.includes(searchValue);

if (nodeFound) {
 setTimeout(() => {
    const someNode = treeModel.getNodeById(item.id);
    if (someNode) { someNode.ensureVisible(); }
  });
  this.temp.push(node);
} else {
  if (item.children && item.children.length) {
    for (let i = 0; i < item.children.length; i++) {
      this.showChildren(node, item.children[i], value, treeModel);
    }
  }
}

}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

salilbajaj picture salilbajaj  路  4Comments

nicolae536 picture nicolae536  路  5Comments

CC84 picture CC84  路  4Comments

carmenbranje picture carmenbranje  路  5Comments

Shadowlauch picture Shadowlauch  路  5Comments