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?
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()andensureVisible()on themfilterTree(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);
}
}
}
}
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);
}