0

1 つの「アプリ」モデルと 2 つのビュー モデルの 2 つのビューを持つ単純なアプリがあります。jQuery イベントのバインドを解除する方法に興味があります。

アプリ ビュー モデルには次のプロパティがあります。

var self = this;    
self.viewModel = ko.observable(null);

最も重要な HTML スニペットはマスター テンプレートにあり、それは次のとおりです。

        <!-- ko if: viewModel -->
    <div data-bind="template:{name:viewModel().template, data:viewModel(), afterRender: viewModel().viewDidRender}"></div>
    <!-- /ko -->

アプリ モデルでは、初期化の後、ビュー モデルが読み込まれます。各ビュー モデルには、レンダリングする html テンプレートを参照する文字列である単純なテンプレート プロパティがあります。テンプレート宣言のデータ項目は、knockoutjs コンテキストを現在のビュー モデルに設定します。afterRender バインディングにより、ViewModel の viewDidRender メソッドが (コンテナーの html 要素をパラメーターとして) 呼び出されることが保証されます。

これは、次のことができることを意味します。

self.viewDidRender = function (parentElement) {
    self.containerElement = parentElement;

    $("html").on("click", function (event) {
        var elements = $(event.target).parents();
        for (var i = 0; i < elements.length; i++)
            if (elements[i] == self.containerElement[0] || elements[i] == self.containerElement) {
                alert("the target exists within the parent element");
                return;
            }
        self.open(false);
    });
};

html 要素のクリック イベントをインターセプトする必要があります。ターゲットがサブビューの親要素内にある場所でイベントが発生した場合は、イベントを無視します。それ以外の場合は、'open' を false に設定します (これは、実際には iOS の PopOvers で見られる簡単な非表示メカニズムの一部です)。

2 つのメイン ビューを切り替えると (この例のビューの viewDidRender は、レンダリングされるたびに呼び出されます)、上記のボディ クリック ハンドラーが複数回バインドされていることがわかりました。

これは JavaScript のメモリ リークだと思います。これらのバインディングはどこでクリーンアップする必要がありますか? $("html").off(...) などを呼び出すことができますが、どこで? テンプレート バインディングの beforeRemove イベントは、純粋なテンプレート アイテムに対しては呼び出されません。foreach バインディングに対してのみです。

「viewWillHide」のようなすべてのサブビュー モデル内に別のメソッドを作成できるので、新しいビューがアプリ モデルの ViewModel プロパティにプッシュされるたびに、ビュー モデルで viewWillHide メソッドを呼び出すことができますこれはうまくいくでしょう。ただし、アプリケーションはここで説明するよりも複雑です。実際には、ビュー モデルとネストされたテンプレートの階層があります。

手動で行うことはオプションです。テンプレート バインディングの afterRender に反対がないのは奇妙に思えます。

それで、あなたは何をしますか?手動アプローチ?viewWillHide メソッドを最上位 (アプリ モデル) から小さなビュー モデルまで階層に伝播するために、ビュー モデル階層全体に一連の変更が発生しますが。

ありがとう

4

1 に答える 1

0

次のコードを使用して、jQuery イベントのバインドを解除してみてください

ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
    //Do cleanup
});

編集:バインディングをラップする方法を示すためのちょっとしたフィドル

http://jsfiddle.net/d6cJG/

問題は、テンプレート バインディングにバインドされた div の addDisposeCallback がコールバックをトリガーしないことです。これは、その要素が決して変更されず、テンプレートが変更されるとその内部の要素が変更されるためです。

ここでコールバックはhttp://jsfiddle.net/d6cJG/1/と呼ばれます

于 2013-02-12T19:17:11.513 に答える