更新:
本周我不得不改进我一年多前编写的代码,并且经过大量测试,这就是我最终得到的结果。这种方法比旧方法快很多,我的意思是很多!使用5 个节点和 5100 行的节点深度进行测试,此数据准备大约需要 1.3s,但如果您不需要不区分大小写的搜索,则删除 toLowerCase 将把时间减半到600ms左右。当搜索字符串准备好时,搜索是即时的。
这是来自我们准备数据的 setData 函数
var self = this,
searchProperty = "name";
//if it's a tree grid, we need to manipulate the data for us to search it
if (self.options.treeGrid) {
//createing index prop for faster get
createParentIndex(items);
for (var i = 0; i < items.length; i++) {
items[i]._searchArr = [items[i][searchProperty]];
var item = items[i];
if (item.parent != null) {
var parent = items[item.parentIdx];
while (parent) {
parent._searchArr.push.apply(
parent._searchArr, uniq_fast(item._searchArr)
);
item = parent;
parent = items[item.parentIdx];
}
}
}
//constructing strings to search
//for case insensitive (.toLowerCase()) this loop is twice as slow (1152ms instead of 560ms for 5100rows) .toLowerCase();
for (var i = 0; i < items.length; i++) {
items[i]._search = items[i]._searchArr.join("/").toLowerCase();
items[i]._searchArr = null;
}
//now all we need to do in our filter is to check indexOf _search property
}
在上面的代码中,我使用了一些函数。第一个创建两个属性,一个用于它自己在数组中的位置,第二个parentIdx用于父索引。我不太确定这是否真的提高了性能,但它消除了setData函数中嵌套循环的需要。
在这里真正发挥作用的是uniq_fast,它接受一个数组并删除其中的所有重复项。该方法是此答案中的众多功能之一remove-duplicates-from-javascript-array
function createParentIndex(items) {
for (var i = 0; i < items.length; i++) {
items[i].idx = i; //own index
if (items[i].parent != null) {
for (var j = 0; j < items.length; j++) {
if (items[i].parent === items[j].id) {
items[i].parentIdx = j; //parents index
break;
}
}
}
}
}
function uniq_fast(a) {
var seen = {};
var out = [];
var len = a.length;
var j = 0;
for (var i = 0; i < len; i++) {
var item = a[i];
if (seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
现在,通过所有这些数据准备,我们的过滤器功能实际上变得非常小且易于处理。为每个项目调用过滤器函数,因为我们现在在每个项目上都有_search属性,所以我们只需检查它。如果没有应用过滤器,我们需要确保我们不显示关闭的节点
function treeFilter(item, args) {
var columnFilters = args.columnFilters;
var propCount = 0;
for (var columnId in columnFilters) {
if (columnId !== undefined && columnFilters[columnId] !== "") {
propCount++;
if (item._search === undefined || item._search.indexOf(columnFilters[columnId]) === -1) {
return false;
} else {
item._collapsed = false;
}
}
}
if (propCount === 0) {
if (item.parent != null) {
var dataView = args.grid.getData();
var parent = dataView.getItemById(item.parent);
while (parent) {
if (parent._collapsed) {
return false;
}
parent = dataView.getItemById(parent.parent);
}
}
}
return true;
}
所以,这个问题是很久以前提出的,但如果有人正在寻找这个问题的答案,请使用上面的代码。它很快,但是对代码的任何改进都会非常受欢迎!
编辑结束
旧答案(这很慢):
首先,您必须创建一个与 dataView 一起使用的过滤器函数。只要您键入内容,dataView 就会调用您的函数。将为 dataView 中的每一行调用该函数,并将该行作为item参数传递。返回 false 表示应该隐藏该行,返回 true 表示可见。
查看Tree 示例,过滤器函数如下所示
function myFilter(item, args) {
if (item["percentComplete"] < percentCompleteThreshold) {
return false;
}
if (searchString != "" && item["title"].indexOf(searchString) == -1) {
return false;
}
if (item.parent != null) {
var parent = data[item.parent];
while (parent) {
if (parent._collapsed || (parent["percentComplete"] < percentCompleteThreshold) || (searchString != "" && parent["title"].indexOf(searchString) == -1)) {
return false;
}
parent = data[parent.parent];
}
}
return true;
}
在我第一次尝试这样做时,我试图操纵父级使其不应该被隐藏。问题是我不知道如何取消隐藏它,问题还在于您不知道将按什么顺序过滤行(如果父行是最后一个要过滤的,则父属性为null)
我放弃了这个想法并尝试使用传递给方法的项目,因为这就是它的意图。使用基本父/子树结构的方法是使用递归。
我的解决方案
首先,创建一个包含所有过滤并返回true或false的函数。我使用快速过滤器的固定标题行作为基础,然后向其中添加了我自己的规则。这是我的realFilter函数的一个非常精简的版本,所以你可能需要稍微调整一下。
function realFilter(item, args) {
var columnFilters = args.columnFilters;
var grid = args.grid;
var returnValue = false;
for (var columnId in columnFilters) {
if (columnId !== undefined && columnFilters[columnId] !== "") {
returnValue = true;
var c = grid.getColumns()[grid.getColumnIndex(columnId)];
if (item[c.field].toString().toLowerCase().indexOf(
columnFilters[columnId].toString().toLowerCase()) == -1) { //if true, don't show this post
returnValue = false;
}
}
}
return returnValue;
}
其次,是时候使用递归函数了。如果您不熟悉它们的工作方式,这是棘手的部分。
//returns true if a child was found that passed the realFilter
function checkParentForChildren(parent, allItems, args) {
var foundChild = false;
for (var i = 0; i < allItems.length; i++) {
if (allItems[i].parent == parent.id) {
if (realFilter(allItems[i], args) == false && foundChild == false) //if the child do not pass realFilter && no child have been found yet for this row
foundChild = checkParentForChildren(allItems[i], allItems, args);
else
return true;
}
}
return foundChild;
}
最后,我们实现了原来的过滤功能。这是slickgrid调用的函数,应该注册到 dataView
//registration of the filter
dataView.setFilter(filter);
//the base filter function
function filter(item, args) {
var allRows = args.grid.getData().getItems();
var columnFilters = args.columnFilters;
var grid = args.grid;
var checkForChildren = false;
for (var i = 0; i < allRows.length; i++) {
if (allRows[i].parent == item.id) {
checkForChildren = true;
break;
}
}
for (var columnId in columnFilters) {
if (columnId !== undefined && columnFilters[columnId] !== "") {
var c = grid.getColumns()[grid.getColumnIndex(columnId)];
var searchString = columnFilters[columnId].toLowerCase().trim();
if (c != undefined) {
if (item[c.field] == null || item[c.field] == undefined) {
return false;
}
else {
var returnValue = true;
if (checkForChildren) {
returnValue = checkParentForChildren(item, allRows, args);
if(!returnValue)
returnValue = realFilter(item, args);
}
else
returnValue = realFilter(item, args);
if (item.parent != null && returnValue == true) {
var dataViewData = args.grid.getData().getItems();
var parent = dataViewData[item.parent];
while (parent) {
if (parent._collapsed) {
parent._collapsed = false;
}
parent = dataViewData[parent.parent];
}
}
return returnValue;
}
}
}
}
if (item.parent != null) {
var dataViewData = args.grid.getData().getItems();
var parent = dataViewData[item.parent];
while (parent) {
if (parent._collapsed) {
return false;
}
parent = dataViewData[parent.parent];
}
}
return true;
}
我目前正在研究这个,所以我还没有真正费心改进代码。据我所知,它正在工作,但您可能需要调整filter和realFilter中的一些内容才能使其按预期工作。我今天写了这个,所以它没有在开发阶段进行更多的测试。
注意:如果您想使用另一个输入进行搜索,您可以在该字段上使用$.keyup(),然后将数据传递给标题过滤器。通过这种方式,您可以获得使用列级过滤器的所有功能,即使您不想在这种特殊情况下使用它们。