2

現在、slickgrid を使用してツリービューを実装しています。

私のコードは基本的にこのに基づいています。

私がやろうとしているのは、例にあるものと同様の検索フィルターを取得することですが、これは親だけでなくブランチでも機能します。たとえば、ツリーが次のようになっているとします。

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

数字「1」を検索すると、次のように表示されます。

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

これではなく:

-Parent 1

申し訳ありませんが、表示するコードがありません。どこにもありません。何か案は?ありがとう

4

2 に答える 2

2

アップデート:

今週、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;
}

私は現在これに取り組んでいるので、まだコードを改善する必要はありません。私の知る限りでは機能していますが、期待どおりに機能させるには、 filterrealFilterでいくつかの調整が必要になる場合があります。今日これを書いたので、開発段階以外はテストされていません。

注: 検索に別の入力を使用する場合は、そのフィールドで$.keyup()を使用して、データをヘッダー フィルターに渡すことができます。このようにして、この特定のケースでそれらを使用したくない場合でも、列レベルのフィルターを使用するためのすべての機能を取得します。

于 2015-01-22T16:55:37.897 に答える
1

個人的に私はグループ化の例を使用し、それを複数列の(ネストされた)グループ化にするのにも役立ちました。それを使用すると、まさにあなたが探していることを実行できます...だから、あなたが言ったものを使用する代わりに、インデントのみの場合は、このインタラクティブなグループ化と集計を使用する必要があります。

例には検索が含まれていませんが、私のプロジェクトのように簡単に追加できます。はい、親グループが消えることはありません。マルチグループ化の例では、50,000 行を選択し、[期間、労力主導、パーセントでグループ化] をクリックすると、3 列のグループ化が表示されます:) コピーして検索バーを追加すると、機能するはずです

于 2013-05-30T16:59:50.893 に答える