アップデート:
今週、1 年以上前に書いたコードを改善する必要があり、多くのテストを行った結果、これが最終的な結果になりました。この方法は、古い方法よりもはるかに高速です。5 つのノードと 5100 行のノード深度でテストしたところ、このデータの準備には約 1.3 秒かかりますが、大文字と小文字を区別しない検索が必要ない場合は、toLowerCase を削除すると、その時間の半分の約600ミリ秒になります。検索文字列が準備されると、検索は瞬時に行われます。
これは、データを準備する 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
}
上記のコードでは、いくつかの関数を使用しています。最初のプロパティは 2 つのプロパティを作成します。1 つは配列内の独自の位置用で、2 番目のparentIdxは親インデックス用です。これにより実際にパフォーマンスが向上するかどうかはわかりませんが、setData関数でネストされたループが不要になります。
ここで実際にすべての違いを生むのは、配列を取り、その中のすべての重複を削除するuniq_fastです。メソッドは、この回答の多くの関数の1つです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です)
私はその考えを放棄し、メソッドに渡されたアイテムを操作しようとしました。これが意図されている方法です。基本的な親子ツリー構造を操作する場合の方法は、recursionを使用することです。
私の解決策
まず、すべてのフィルタリングを保持し、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()を使用して、データをヘッダー フィルターに渡すことができます。このようにして、この特定のケースでそれらを使用したくない場合でも、列レベルのフィルターを使用するためのすべての機能を取得します。