Slickgrid Treeview搜索 [英] Slickgrid Treeview Search

查看:103
本文介绍了Slickgrid Treeview搜索的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用slickgrid实现树视图。

I am currently implementing a treeview using slickgrid.

我的代码基本上基于此示例

My code is essentially based on this example.

我要做的是获取搜索过滤器,类似于示例中的那个,但它在分支和父母上工作。例如,如果树看起来像这样:

What I am trying to do is get a search filter, similar to the one in the example, but which works on the branches as well as the parents. For instance if a tree looks like this:

-Parent 1
  -branch 1
   -sub_branch 1
  -branch 2
-Parent 2
  -branch 1
  -branch 2

我搜索它应该显示的数字'1':

and I search for the number '1' it should show this:

-Parent 1
  -branch 1
   -sub_branch 1
  -branch 2
-Parent 2
  -branch 1

而不是:

-Parent 1

抱歉,我没有任何代码可以显示,我没有任何地方。有任何想法吗?谢谢

Sorry I don't have any of my code to show, I've not got anywhere. Any ideas? Thanks

推荐答案

更新:

我不得不改进本周一年前写的代码,经过大量的测试,这就是我最终的结果。这个方法比旧的方法快很多,我的意思是很多!使用节点深度为5个节点和5100行进行测试,此数据准备大约需要 1.3s ,但如果您不需要不区分大小写的搜索,那么删除toLowerCase的时间将减半到 600ms 。准备搜索字符串时,搜索是即时的。

I had to improve the code i wrote over a year ago this week, and with alot of testing, this is what i ended up with. This method is alot faster than the old one, and i mean alot! Tested with a node depth of 5 nodes and 5100 rows this data preparation takes around 1.3s, but if you don't need case insensitive search, removing toLowerCase will half that time to around 600ms. When the searchstrings are prepared, the search is instant.

这是来自我们的setData函数,我们准备数据

This is from our setData function where we prepare the data

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 函数中的嵌套循环。

In the code above, I use some functions. The first one creates two properties, one for its own position in the array, and the second parentIdx for parents index. I'm not so sure if this actually speeds up the performance, but it removes the need for a nested loop in the setData function.

实际上与众不同的是 uniq_fast ,它接受一个数组并删除其中的所有重复项。该方法是此答案中的众多功能之一从javascript-array中删除重复项

The one that actually makes all the difference here is the uniq_fast, which takes an array and removes all the duplicates in it. The method is one of the many functions from this answer 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 属性,我们只需检查它。如果没有应用过滤器,我们需要确保我们不显示闭合节点

Now with all this preparation of the data, our filter function actually becomes pretty small and easy to handle. The filter function is called for each item, and as we now have the _search property on each item, we just check that. If no filter applied we need to make sure that we don't show closed nodes

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;
}

所以,很久以前就问过这个问题,但如果有人在寻找回答这个问题,使用上面的代码。这很快,但代码的任何改进都会受到很多评价!

So, the question was asked long ago, but if someone is looking for an answer for this, use the code above. It's fast, but any improvements of the code would be much appritiated!

END OF EDIT

旧答案(这很慢):

首先,您必须创建过滤器功能您与dataView一起使用。键入内容后,dataView将立即调用您的函数。将为dataView中的每一行调用该函数,并将该行作为 item 参数传递。返回false表示该行应该隐藏,而true表示可见。

As a start, you have to create a filter function that you use with your dataView. The dataView will call your function as soon as you type something. The function will be called for each row in the dataView, passing the row as the item parameter. Returning false indicates that the row should be hidden, and true for visible.

查看树例子,过滤函数看起来像这样

Looking at the Tree example, the filter function looks like this

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

In my first attempt to do this, I tried to manipulate the parent so that it should not be hidden. The problem is that i have no clue how to unhide it, and the problem is also that you don't know in which order the rows will be filtered (if the parent row is the last to be filtered, parent property is null)

我放弃了这个想法并尝试使用传递给方法的项目,因为这是它的意图。使用基本父/子树结构时的方法是使用递归

I abandoned that thought and tried to work with the item passed into the method, as this is how it's intended. The way to do it when working with basic parent/child tree structures is to use recursion.

首先,创建一个包含所有过滤并返回 true false 的函数。我将使用固定标题行作为快速过滤器作为基地然后添加我自己的规则。
这是我的 realFilter 函数的真正精简版本,因此您可能需要稍微调整一下。

To start, create a function that holds all the filtering and returns true or false. I've used fixed header row for fast filters as a base and then added my own rules to it. This is a really stripped down version of my realFilter function, so you might need to tweak it a little bit.

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;
}

其次,递归函数是时候了。如果你不熟悉它们的工作方式,这是一个棘手的部分。

Secondly, it's time for the recursive function. This is the tricky part if you'r not familiar with how they work.

//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

At last, we implement the original filter function. This is the function that is called by slickgrid and should be registered to the 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;
}

我正在研究这个问题,所以我并没有真正想要改进代码呢。据我所知,它正在发挥作用,但您可能需要在过滤器 realFilter 中调整一些内容,以使其按预期工作。我今天写了这个,所以它没有在开发阶段进行测试。

I'm currently working on this so I have not really bothered to improve the code yet. It is working as far as i know, but you may have to tweak some things in filter and realFilter to get it to work as you expect. I wrote this today so it's not tested more than under the development phase.

注意:
如果你想使用其他输入进行搜索,你可以使用该字段上的 $。keyup(),然后将数据传递到标头过滤器。这样您就可以获得使用列级过滤器的所有功能,即使您不想在这种特定情况下使用它们。

Note: If you want to use another input for your search you can just use $.keyup() on that field and then pass the data to the header filter. This way you get all the functionality to use column-level filters, even if you don't want to use them in this particular case.

这篇关于Slickgrid Treeview搜索的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆