Hi,
I'm trying to filter a kendo-ui treeView by text and with button to display or not some root in my tree.
I already use this to filter the tree view with input and it work perfectly, and on top of that i want to filter the root node.
I use treeview react component
When i click some button, I set the hidden attribute to the root node and I re-filter it.
public handleClick() {
const dataSource: kendo.data.DataSource = $(
"[data-role='treeview']"
).data(
"kendoTreeView"
).dataSource;
const data = dataSource
instanceof
kendo.data.HierarchicalDataSource && dataSource.data();
_.forEach(data as kendo.data.ObservableArray, (item) => {
if
(item.type ===
this
.props.controlNode) {
item.hidden =
this
.props.filter;
}
});
dataSource.filter({ field:
"hidden"
, operator:
"neq"
, value:
true
});
dataSource.filter().logic =
"and"
;
}
My buttons have a `controlNode` attribute to compare to the type attribute inside my dataSource. I iterate through the first child of my dataSource and if the type match I set the hidden attribute true or false depending on the filter state.
So my problem is, `dataSource.filter({ field: "hidden", operator: "neq", value: true });` don't filter the dataSource. but if i use the operator `eq` instead of `neq` I have the opposite behavior, but it's working...
I don't know if I'm doing something wrong.
15 Answers, 1 is accepted
Thank you for all of the details.
Based on the code the approach should be working.
As this seems as a strange behavior could you share an example with us reproducing the issue and we will be happy to investigate it.
If sharing an example is currently, not possible please check if reversing the logic will lead to the expected result. For example, if "eq" operator is working, but it is showing the opposite results, I can suggest to either change the filter value to false instead of true or to set "!this.props.filter" instead of "this.props.filter".
I hope one of the approaches to help reserve the logic and achieve the desired result.
Regards,
Stefan
Progress Telerik
Hello, Stefan,
I tried reverse the logic and didn't work.
I build a little setup, join here.
I just have a little bug, the
$(
"[data-role='treeview']"
).data(
"kendoTreeView"
)
return undefined. I have no idea why, since it's working perfectly fine in app.
Thank,
Vincent.
import React from
"react"
;
import * as ReactDOM from
"react-dom"
;
import _ from
"lodash"
;
import $ from
"jquery"
;
import
"@progress/kendo-theme-default/dist/all.css"
;
import { TreeView } from
"@progress/kendo-treeview-react-wrapper"
;
import { IconButton, TextField } from
"@material-ui/core"
;
import { FontAwesomeIcon } from
"@fortawesome/react-fontawesome"
;
import { library } from
"@fortawesome/fontawesome-svg-core"
;
import { faGlobe, faUser, faCube } from
"@fortawesome/free-solid-svg-icons"
;
enum TreeType {
AccountRoot =
"AccountRoot"
,
Account =
"Account"
,
PackageRoot =
"PackageRoot"
,
Package =
"Package"
,
EnvironmentRoot =
"EnvironmentRoot"
,
Environment =
"Environment"
,
}
interface ITreeViewNode {
id: string;
text: string;
type: TreeType;
expanded?: boolean;
items?: ITreeViewNode[];
}
interface IDemoProps {
data: ITreeViewNode[];
}
function
filterTreeView(dataSource: kendo.data.DataSource, query: string): boolean {
let hasVisibleChildren =
false
;
const data = dataSource
instanceof
kendo.data.HierarchicalDataSource && dataSource.data();
_.forEach(data as kendo.data.ObservableArray, (item) => {
const text: string = item.text.toLowerCase();
const itemVisible =
query ===
""
// query is empty
|| text.indexOf(query as string) >= 0;
// item text matches query
const anyVisibleChildren = filterTreeView(item.children, query);
// pass true if parent matches
hasVisibleChildren = hasVisibleChildren || anyVisibleChildren || itemVisible;
item.hidden = !itemVisible && !anyVisibleChildren;
});
if
(data) {
// re-apply filter on children
dataSource.filter({ field:
"hidden"
, operator:
"neq"
, value:
true
});
}
return
hasVisibleChildren;
}
class Demo extends React.Component<IDemoProps> {
public state = {
userStateFilter :
false
,
packageStateFilter :
false
,
environmentStateFilter :
false
,
};
public constructor(props: IDemoProps) {
super
(props);
this
.onSelect =
this
.onSelect.bind(
this
);
this
.handleTextFilter =
this
.handleTextFilter.bind(
this
);
this
.handleClickUser =
this
.handleClickUser.bind(
this
);
this
.handleClickPackage =
this
.handleClickPackage.bind(
this
);
this
.handleClickEnvironment =
this
.handleClickEnvironment.bind(
this
);
}
public onSelect = (e: kendo.ui.TreeViewSelectEvent) => {
if
(e.node !== undefined) {
const treeView = $(
"[data-role='treeview']"
).data(
"kendoTreeView"
);
treeView.toggle(e.node);
// @ts-ignore : custom attribute (type)
console.log(treeView.dataItem(e.node).id +
" : "
+ treeView.dataItem(e.node).type);
}
}
public handleTextFilter(e: React.ChangeEvent<HTMLInputElement>) {
const treeData = $(
"[data-role='treeview']"
).data(
"kendoTreeView"
);
console.log(treeData);
// const value = e.target.value.toLocaleLowerCase();
// filterTreeView(treeData, value);
}
public handleClickUser() {
this
.state.userStateFilter = !
this
.state.userStateFilter;
const dataSource: kendo.data.DataSource = $(
"[data-role='treeview']"
).data(
"kendoTreeView"
).dataSource;
const data = dataSource
instanceof
kendo.data.HierarchicalDataSource && dataSource.data();
_.forEach(data as kendo.data.ObservableArray, (item) => {
if
(item.type === TreeType.AccountRoot) {
item.hidden =
this
.state.userStateFilter;
}
});
dataSource.filter({ field:
"hidden"
, operator:
"neq"
, value:
true
});
dataSource.filter().logic =
"and"
;
}
public handleClickPackage() {
this
.state.packageStateFilter = !
this
.state.packageStateFilter;
const dataSource: kendo.data.DataSource = $(
"[data-role='treeview']"
).data(
"kendoTreeView"
).dataSource;
const data = dataSource
instanceof
kendo.data.HierarchicalDataSource && dataSource.data();
_.forEach(data as kendo.data.ObservableArray, (item) => {
if
(item.type === TreeType.PackageRoot) {
item.hidden =
this
.state.packageStateFilter;
}
});
dataSource.filter({ field:
"hidden"
, operator:
"neq"
, value:
true
});
dataSource.filter().logic =
"and"
;
}
public handleClickEnvironment() {
this
.state.environmentStateFilter = !
this
.state.environmentStateFilter;
const dataSource: kendo.data.DataSource = $(
"[data-role='treeview']"
).data(
"kendoTreeView"
).dataSource;
const data = dataSource
instanceof
kendo.data.HierarchicalDataSource && dataSource.data();
_.forEach(data as kendo.data.ObservableArray, (item) => {
if
(item.type === TreeType.EnvironmentRoot) {
item.hidden =
this
.state.environmentStateFilter;
}
});
dataSource.filter({ field:
"hidden"
, operator:
"neq"
, value:
true
});
dataSource.filter().logic =
"and"
;
}
public render() {
return
(
<div>
<TextField id=
"input-search"
label=
"Search"
onChange={
this
.handleTextFilter} />
<IconButton color=
"inherit"
onClick={
this
.handleClickUser}>
<FontAwesomeIcon icon=
"user"
size=
"xs"
/>
</IconButton>
<IconButton color=
"inherit"
onClick={
this
.handleClickPackage}>
<FontAwesomeIcon icon=
"cube"
size=
"xs"
/>
</IconButton>
<IconButton color=
"inherit"
onClick={
this
.handleClickEnvironment}>
<FontAwesomeIcon icon=
"globe"
size=
"xs"
/>
</IconButton>
<TreeView
loadOnDemand={
false
}
select={
this
.onSelect}
dataSource={
this
.props.data}/>
</div>
);
}
}
class App extends React.Component {
public data: ITreeViewNode[] = [
{
id:
"UserRoot"
,
text:
"UserRoot"
,
type: TreeType.AccountRoot,
expanded:
false
,
items: [{
id:
"User1"
,
text:
"User1"
,
type: TreeType.Account,
},
{
id:
"User2"
,
text:
"User2"
,
type: TreeType.Account,
}],
},
{
id:
"PackageRoot"
,
text:
"PackageRoot"
,
type: TreeType.PackageRoot,
expanded:
false
,
items: [{
id:
"Package1"
,
text:
"Package1"
,
type: TreeType.Package,
},
{
id:
"Package2"
,
text:
"Package2"
,
type: TreeType.Package,
}],
},
{
id:
"EnvironmentRoot"
,
text:
"EnvironmentRoot"
,
type: TreeType.EnvironmentRoot,
expanded:
false
,
items: [{
id:
"Environment1"
,
text:
"Environment1"
,
type: TreeType.Environment,
},
{
id:
"Environment2"
,
text:
"Environment2"
,
type: TreeType.Environment,
}],
},
];
public render() {
return
(
<Demo
data={
this
.data}
/>
);
}
}
library.add(faGlobe);
library.add(faUser);
library.add(faCube);
ReactDOM.render(
<App />,
document.getElementById(
"app"
),
);
Here a little demo : https://i.imgur.com/wBaJWj7.gifv
Thank you for the code.
After inspecting the case I think I noticed that the recursion is not used.
In our demo, notice how the filter function is called recursively in order to filter the children as well:
var
anyVisibleChildren = filter(item.children, itemVisible || query);
// pass true if parent matches
Currently, the used logic is only searching inside the parent elements.
Also, I used the inverted logic, setting in which nodes are not hidden and only showing them:
https://next.plnkr.co/edit/vLwgkkicwzC7bTNC
If the issue still occurs inside the real application, please share a runnable version of it so I can closely invest it.
Regards,
Stefan
Progress Telerik
Hello, Stephan.
Thank you for your response.
I didn't use recursivity because with the filter "neq" I thought I will not need it.
With the logic use in your Plunker, it hide all the child. I don't really want that.
So, I guess, I have to filter all the child and set the "hidden" field to "false".
I'll use this logic, but why the "neq" operator didn't work properly in the first place ? Is it a bug ? Or the logic use is not ok ?
Regards
Vincent.
I can assume that it is not working due to the way the not equal operator is working with true and false.
I made some test but it was working correctly in the standard cases.
As this is indeed not expected and event working in our example, we will make additional tests and log an issue to fix it if needed.
Thank you for the collaboration.
Regards,
Stefan
Progress Telerik
After more test, we located the reason for this results.
The reason is the child nodes, as they are not filtered with this condition, and as they are visible, even if the parent has to be hidden, it will be shown in order to show the child elements. If you have even one visible child element the parent element has to be visible as well.
This is why it is working in our demo as there the child nodes are filtered as well using recursion.
If the recursion filtering is implemented in the real application, then the approach with not equal should works as expected as well.
Let me know if you need more details on why this occurs.
Regards,
Stefan
Progress Telerik
I tried to use the logic in this article https://docs.telerik.com/kendo-ui/controls/navigation/treeview/how-to/filtering/filter-out-search-results
but I keep getting an error "Uncaught TypeError: filter is not a function" caused by the recursive call within my function
Could you please give me a suggestion or a solution in order to be able to filter a treeView based on the values selected by the user from a multi select field (means the input is not a single text but an array of values )
thanks a lot
I will be happy to assist in resolving the issue.
Could you please share with us how the code looks like and on which line the error occurs.
If the error occurs on this line, please ensure that the filter function is correctly bound to "this" as otherwise it may not be recognized as a function:
var
anyVisibleChildren = filter(item.children, itemVisible || query);
// pass true if parent matches
filter = (dataSource, query) => {
var
hasVisibleChildren =
false
;
var
data = dataSource
instanceof
kendo.data.HierarchicalDataSource && dataSource.data();
for
(
var
i = 0; i < data.length; i++) {
var
item = data[i];
var
text = item.text.toLowerCase();
var
itemVisible =
query ===
true
// parent already matches
|| query ===
""
// query is empty
|| text.indexOf(query) >= 0;
// item text matches query
var
anyVisibleChildren =
this
.filter(item.children, itemVisible || query);
// pass true if parent matches
hasVisibleChildren = hasVisibleChildren || anyVisibleChildren || itemVisible;
item.hidden = !itemVisible && !anyVisibleChildren;
}
Regards,
Stefan
Progress Telerik
Now the only thing left is that when I try to filter using a value in the 2nd or 1st level , only that node is show the children are hidden , i dont know what's causing this behavior , because when i filter on one of the children it displays the upper nodes
I`m glad that there is progress on this filtering functionality.
As for the new issue, could you please share the filter implementation or a full example, so we can observe what could be causing this?
I can assume that the recursion logic is removing the children of the filtered item as well, but observing the implementation we give as a hint why this is happening.
Regards,
Stefan
Progress Telerik
Hi Stefan,
I am going to use a filter, where it will be in a tree structure, from that tree list i will choose the data. similar to JS tree in jquery. attached an image as how im expecting. Is it possible to use that in kendo react grid?
Hello, Tamilarasan,
If there will be tree structure we recommend our TreeList component, as it is very similar to the Grid functionalities, but it is showing tree data:
https://www.telerik.com/kendo-react-ui/components/treelist/
The screenshot is also showing a component that looks like our TreeView component, so that could also be a good option:
https://www.telerik.com/kendo-react-ui/components/treeview/
Regards,
Stefan
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.
Hello, Tamilarasan,
The Grid is designed to be used with tabular data and shows a tree only when grouped, but that is a little different type of tree.
This is why we have the TreeList and TreeView components.
The component from the screenshot is nearly 100% similar to the TreeView.
Is there any reason why the TreeList or the TreeView are not possible in this case?
Regards,
Stefan
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.