KendoUI фильтр TreeView
Я использую древовидную структуру KendoUI и хочу дать пользователю возможность отфильтровать ее. Есть даже демонстрация, которая делает то, что я хочу (http://demos.kendoui.com/web/treeview/api.html)
Проблема в том, что фильтр применяется только к 1-й иерархии TreeView, поэтому, если текст фильтра присутствует в дочернем элементе, но не в родительском элементе, дочерний элемент не будет отображаться.
Пример:
- Элемент 1
- Пункт 2
- Элемент xzy
- Пункт abc
Если текст поиска будет "abc", элемент не будет отображаться. Вместо этого я хотел бы получить следующий результат:
- Пункт 2
- Пункт abc
Кто-нибудь знает как это сделать? Это код, который я использую:
var tree_view_data = new kendo.data.HierarchicalDataSource({
transport: {
read: {
url: "getall/items",
dataType: "json"
}
},
schema: {
model: {
children: "ChildItems"
}
}
});
//init tree view itself
var $treeview = $("#div-treeview").kendoTreeView({
dataSource: tree_view_data,
dataTextField: [ "Text", "ChildrenText" ]
});
//allow filter of navigation tree
var refreshTree = function () {
tree_view_data.filter({
field: "Text", //if I would use "ChildrenText" here nothing be displayed at all if filtertext is set
operator: "contains",
value: $("#tree-text-search").val()
});
};
$("#tree-text-search").change(refreshTree).keyup(refreshTree);
6 ответов
Обновление 2016-01-13: теперь имеется раздел справки, в котором показано, как выполнять фильтрацию TreeView на основе пользовательской строки.
Необходимо вручную отфильтровать дочерние источники данных, чтобы отображались только необходимые узлы. Имея разные dataTextField
s для разных уровней затрудняет понимание, поэтому этот код использует text
только поле Кроме того, поскольку эта фильтрация выполняется на стороне клиента, предполагается, что вы загрузили все узлы.
var treeview = $("#treeview").data("kendoTreeView"),
item = treeview.findByText("Item 1.3"), // find the node that will be shown
dataItem = treeview.dataItem(item),
nodeText = dataItem.text;
// loop through the parents of the given node, filtering them to only one item
while (dataItem.parentNode()) {
dataItem = dataItem.parentNode();
dataItem.children.filter({ field: "text", operator: "contains", value: nodeText });
nodeText = dataItem.text;
}
treeview.dataSource.filter({ field: "text", operator: "contains", value: nodeText });
Я нашел способ сделать это, просто используя селекторы jQuery, чтобы скрыть и показать необходимые дочерние узлы.
Прежде всего, когда вы создаете свое древовидное представление, добавьте этот параметр в ваши параметры:
loadOnDemand: false
Таким образом, дерево будет отображать весь HTML-код ваших дочерних узлов перед запросом, что позволит вам использовать jQuery для навигации по ним.
Вот код jQuery, который у меня работает, который отфильтровывает не совпадающие узлы, открывает группу совпадающих узлов и показывает их.
$("#searchTextInputField").keyup(function () {
var filterText = $("#searchTextInputField").val();
if(filterText !== "") {
$("#myTree .k-group .k-group .k-in").closest("li").hide();
$("#myTree .k-group .k-group .k-in:contains(" + filterText + ")").each(function() {
$(this).closest("ul").show();
$(this).closest("li").show();
});
} else {
$("#myTree .k-group").find("ul").hide();
$("#myTree .k-group").find("li").show();
}
});
Для более чем 4-х уровней переберите всех родителей типа UL и LI и вызовите show().
$("#filterText").keyup(function (e) {
var filterText = $(this).val();
if (filterText !== "") {
$("#treeview-standards .k-group .k-group .k-in").closest("li").hide();
$("#treeview-standards .k-group .k-group .k-in:contains(" + filterText + ")").each(function () {
$(this).parents("ul, li").each(function () {
$(this).show();
});
});
} else {
$("#treeview-standards .k-group").find("ul").hide();
$("#treeview-standards .k-group").find("li").show();
}
});
Эта версия выполняет поиск по всему дереву, нечувствительна к регистру и скрывает узлы, которые не содержат поисковый запрос (jQuery 1.8+).
$("#search").keyup(function (e) {
var query = $(this).val();
if (query !== "") {
$("#tree-view .k-in").closest("li").hide();
$("#tree-view .k-item .k-in:Contains(" + query + ")").each(function () {
$(this).parents("ul, li").each(function () {
$(this).show();
});
});
} else {
$("#tree-view .k-group").find("ul").hide();
$("#tree-view .k-group").find("li").show();
}
});
jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function (arg) {
return function (elem) {
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
Прежде всего. KendoTreeView - это очень низкоуровневый элемент управления по сравнению с Teleriks RadDropDownTree из ASP.NET http://www.telerik.com/help/aspnet-ajax/dropdowntree-overview.html (я имею в виду js, конечно!) в jquery/kendo... необходимо улучшить этот фильтр, поэтому, если вы предпочитаете правильную фильтрацию по dataitem вместо "findByText", это делает:
.1) находит все элементы данных.2) проверяет ваши условия (здесь строчные буквы содержат значение / текст) .3) помечает элемент, помечает родителей.4) очищает, удаляет узлы, оставленные в дереве родителем
that.nodeFilter = { logic: "or", filters: [] };
that.nodeFilter.filters.push({ field: "hidden", operator: "eq", value: false });
tree.element.find(".k-in").each(function () {
var dItem = tree.dataItem($(this).closest("li"));
dItem.hidden = false;
if (dItem[that.options.dataValueField].toLowerCase().indexOf(searchTerm) != -1 ||
dItem[that.options.dataTextField].toLowerCase().indexOf(searchTerm) != -1) {
that.nodeFilter.filters.push({ field: that.options.dataValueField, operator: "eq", value: dItem[that.options.dataValueField] })
while (dItem.parentNode()) {
dItem = dItem.parentNode();
dItem.hidden = false;
that.nodeFilter.filters.push({ field: that.options.dataValueField, operator: "eq", value: dItem[that.options.dataValueField] })
}
} else {
dItem.hidden = true;
}
});
tree.dataSource.filter(that.nodeFilter);
tree.element.find(".k-in").each(function () {
var node = $(this).closest("li");
var dataItem = tree.dataItem(node);
if (dataItem.hidden) {
tree.remove(node);
}
});
Если я хорошо прочитал вопрос, речь идет о фильтрации данных в представлении, а не о самом древовидном представлении. Это может быть сделано путем рекурсии.
Пример рекурсии, который работает:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kendo UI Snippet</title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.1.117/styles/kendo.common.min.css"/>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.1.117/styles/kendo.rtl.min.css"/>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.1.117/styles/kendo.silver.min.css"/>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.1.117/styles/kendo.mobile.all.min.css"/>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2018.1.117/js/kendo.all.min.js"></script>
</head>
<body>
<div class="demo-section k-content">
<div id="treeview1"></div>
<div id="showit"></div>
<div id="treeview2"></div>
</div>
<script>
//
// Define hierarchical data source
//
var mydata = new kendo.data.HierarchicalDataSource({
name: "Food", items: [
{ name: "Meat", items:
[
{ name: "Pork" },
{ name: "Beef" }
]
},
{ name: "Vegetables", items:
[
{ name: "Pepper" }
]
}
]
});
//
// When debugging
//
var debug=false;
//
// Find and return Item when found.
//
function FindByName(items, myName)
{
//Query definition
var query = kendo.data.Query.process(items, {
filter: {
logic: "or",
filters: [{
field: "name",
value: myName,
operator: "eq"
}]
}
});
if (debug) $("#showit").html($("#showit").html()+" found:" + JSON.stringify(query.data));
//
// return Item when found.
//
if (query.data != "")
return query.data; //ready
else
{
//
// if sub-items, search further
//
for (let i=0; i<items.length; i++)
{
if (debug) $("#showit").html($("#showit").html()+" test:" + JSON.stringify(items[i]));
if (items[i].items!=null)
{
if (debug) $("#showit").html($("#showit").html()+" search sub....");
var r = FindByName(items[i].items, myName);
if (r!=null) return r; //ready, else continue searching further
};
}
}
if (debug) $("#showit").html($("#showit").html()+" not found.");
return null; //nothing found.
}
//
// print the input
//
$("#showit").html($("#showit").html()+" Food:" + JSON.stringify(mydata.options.items));
//
// print the result
//
var ret=FindByName(mydata.options.items,"Beef");
$("#showit").html($("#showit").html()+"<p> Beef:" + JSON.stringify(ret));
$("#treeview1").kendoTreeView({
dataSource: mydata.options.items,
dataTextField: ["name"]
});
ret=FindByName(mydata.options.items,"Meat");
$("#showit").html($("#showit").html()+"<p> Meat:" + JSON.stringify(ret));
ret=FindByName(mydata.options.items,"Pepper");
$("#showit").html($("#showit").html()+"<p> Pepper:" + JSON.stringify(ret));
ret=FindByName(mydata.options.items,"Vegetables");
$("#showit").html($("#showit").html()+"<p> Vegetables:" + JSON.stringify(ret));
//
// Example: bind return value [ret] to treeview.
//
$("#treeview2").kendoTreeView({
dataSource: ret,
dataTextField: ["name"]
});
</script>
</body>
</html>