Knockout.jsで監視可能なバインディングをクリア/削除するにはどうすればよいですか?


113

ユーザーが複数回実行できるWebページに機能を構築しています。ユーザーのアクションを通じて、ko.applyBindings()を使用してオブジェクト/モデルが作成され、HTMLに適用されます。

データバインドされたHTMLは、jQueryテンプレートを介して作成されます。

ここまでは順調ですね。

2番目のオブジェクト/モデルを作成してこの手順を繰り返し、ko.applyBindings()を呼び出すと、2つの問題が発生します。

  1. マークアップには、以前のオブジェクト/モデルと新しいオブジェクト/モデルが表示されます。
  2. オブジェクト/モデルのプロパティの1つに関連するjavascriptエラーが発生しますが、マークアップではレンダリングされます。

この問題を回避するには、最初のパスの後でjQueryの.empty()を呼び出して、すべてのデータバインド属性を含むテンプレート化されたHTMLを削除し、DOMに存在しないようにします。ユーザーが2番目のパスのプロセスを開始すると、データにバインドされたHTMLがDOMに再度追加されます。

しかし、私が言ったように、HTMLがDOMに再度追加され、新しいオブジェクト/モデルに再バインドされると、最初のオブジェクト/モデルからのデータがまだ含まれ、まだ発生しないJSエラーが発生します最初のパスの間。

結論は、マークアップがDOMから削除されていても、Knockoutがこれらのバインドされたプロパティを保持していることです。

したがって、私が探しているのは、これらのバインドされたプロパティをKnockoutから削除する方法です。観測可能なモデルがなくなったことをノックアウトに伝えます。これを行う方法はありますか?

編集

基本的なプロセスは、ユーザーがファイルをアップロードすることです。次にサーバーはJSONオブジェクトで応答し、データにバインドされたHTMLがDOMに追加されます。次に、JSONオブジェクトモデルがこのHTMLにバインドされます。

mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);

ユーザーがモデルでいくつかの選択を行うと、同じオブジェクトがサーバーにポストバックされ、データバインドされたHTMLがDOMから削除され、次のJSを取得します

mn.AccountCreationModel = null;

ユーザーがもう一度これを実行する場合は、これらのすべてのステップが繰り返されます。

私はコードがjsFiddleデモを行うにはあまりにも「関与」していると思います。


特に同じ包含dom要素でko.applyBindingsを複数回呼び出すことはお勧めしません。あなたが望むものを達成する別の方法があるかもしれません。ただし、より多くのコードを提供する必要があります。可能であればjsfiddleを含めてください。
madcapnmckay 2012

init適用するデータを渡す関数を公開しないのはなぜですか?
KyorCode 2014年

回答:


169

メモリ内のオブジェクトを破棄するために、DOM要素でノックアウトのクリーンノードメソッドを呼び出してみましたか?

var element = $('#elementId')[0]; 
ko.cleanNode(element);

次に、新しいビューモデルでその要素のみにノックアウトバインディングを再度適用すると、ビューバインディングが更新されます。


33
これは機能します-ありがとう。ただし、この方法に関するドキュメントは見つかりません。
awj 2012

私もこのノックアウトユーティリティ関数に関するドキュメントを検索しました。ソースコードからわかるようdeleteに、dom要素自体の特定のキーを呼び出しています。これには、明らかにすべてのノックアウトマジックが格納されています。誰かがドキュメンテーションに関する情報源を持っているなら、私は大いに義務付けられるでしょう。
Patrick M

2
あなたはそれを見つけることができません。私はkoユーティリティ関数に関するドキュメントを高低で検索しましたが、存在しません。このブログ投稿は最も近いものですが、ko.utilsのメンバーのみを対象としています。knockmeout.net
Nick Daniels

1
以下の私の回答に示すように、イベントを手動で削除することもできます。
Michael Berkompas

1
@KodeKreachor私はすでに以下の作業例を投稿していました。私の要点は、代わりにViewModel内でデータを解放する一方で、バインディングをそのままにしておく方がよいということでした。そうすれば、アンバインド/再バインドを処理する必要がなくなります。ドキュメントに記載されていないメソッドを使用してDOMから直接手動でバインドを解除するよりもクリーンなようです。それを超えて、CleanNodeはイベントハンドラーを解放しないため問題があります(詳細についてはこちらの回答を参照してください:stackoverflow.com/questions/15063794/…
Zac

31

私が取り組んでいるプロジェクトko.unapplyBindingsでは、jQueryノードと削除ブール値を受け入れる単純な関数を作成しました。ko.cleanNodeメソッドはそれを処理しないため、最初にすべてのjQueryイベントをアンバインドします。メモリリークをテストしましたが、問題なく動作するようです。

ko.unapplyBindings = function ($node, remove) {
    // unbind events
    $node.find("*").each(function () {
        $(this).unbind();
    });

    // Remove KO subscriptions and references
    if (remove) {
        ko.removeNode($node[0]);
    } else {
        ko.cleanNode($node[0]);
    }
};

ただ1つの注意点として、ko.cleanNode()HTML全体を置き換えるのではなく、呼び出したばかりのものへの再バインドをテストしていません。
Michael Berkompas

4
あなたのソリューションは他のすべてのイベントバインディングもアンバインドしませんか?koのイベントハンドラーのみを削除する可能性はありますか?
lordvlad

koコアを変更せずに
lordvlad

1
確かに、私が知る限り、コアの変更なしではそれは不可能です。私がここで取り上げたこの問題を参照してください:github.com/SteveSanderson/knockout/issues/724
Michael Berkompas

ごくまれに自分自身でdomに触れてはいけないというKOの考えではないですか この回答はdomをループするため、私の使用例では確かに使用できません。
ブロージー2013年

12

あなたはノックアウトオファーを結合して使用して試みることができる: http://knockoutjs.com/documentation/with-binding.html アイデアは一度バインディングを適用し使用して、いつでもあなたのデータの変更、ちょうどあなたのモデルを更新することです。

トップレベルのビューモデルstoreViewModel、cartViewModelで表されるカート、およびそのカート内のアイテムのリスト(cartItemsViewModelなど)があるとします。

トップレベルのモデルであるstoreViewModelをページ全体にバインドします。次に、カートまたはカートアイテムを担当するページの部分を分離できます。

cartItemsViewModelが次の構造を持つと仮定します。

var actualCartItemsModel = { CartItems: [
  { ItemName: "FirstItem", Price: 12 }, 
  { ItemName: "SecondItem", Price: 10 }
] }

cartItemsViewModelは、最初は空にすることができます。

手順は次のようになります。

  1. HTMLでバインディングを定義します。cartItemsViewModelバインディングを分離します。

      
        <div data-bind="with: cartItemsViewModel">
          <div data-bind="foreach: CartItems">
            <span data-bind="text: ItemName"></span>
            <span data-bind="text: Price"></span>
          </div>
        </div>
      
    
  2. ストアモデルはサーバーから取得されます(または他の方法で作成されます)。

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. トップレベルのビューモデルに空のモデルを定義します。次に、そのモデルの構造を実際のデータで更新できます。

      
        storeViewModel.cartItemsViewModel = ko.observable();
        storeViewModel.cartViewModel = ko.observable();
     
    
  4. トップレベルのビューモデルをバインドします。

    ko.applyBindings(storeViewModel);

  5. cartItemsViewModelオブジェクトが使用可能な場合、それを以前に定義されたプレースホルダーに割り当てます。

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

カートのアイテムをクリアしたい場合: storeViewModel.cartItemsViewModel(null);

Knockoutはhtmlを処理します。つまり、モデルが空ではなく、divのコンテンツ(「バインディング付き」のコンテンツ)が消えたときに表示されます。


9

検索ボタンをクリックするたびにko.applyBindingを呼び出す必要があり、フィルターされたデータがサーバーから返されます。この場合、ko.cleanNodeを使用せずに次の作業を行います。

私が経験したように、foreachをテンプレートで置き換えると、collections / observableArrayの場合は問題なく機能するはずです。

このシナリオが役立つ場合があります。

<ul data-bind="template: { name: 'template', foreach: Events }"></ul>

<script id="template" type="text/html">
    <li><span data-bind="text: Name"></span></li>
</script>

1
私は同様の問題を解決するために少なくとも4時間を費やしてきましたが、私にはaamirの解決策のみが機能します。
Antonin Jelinek 2013

@AntoninJelinek私のシナリオで経験した他のこと、htmlを完全に削除し、動的に追加して絶対にすべてを削除します。たとえば、私は$ .AjaxでKnockoutコードコンテナーdiv <div id = "knockoutContainerDiv"> </ div>を持っています。成功結果サーバーメソッドが$( "#knockoutContainerDiv")。children.remove(); /を呼び出すたびに、 / removeコンテンツのメソッドを呼び出して、ダイナミックHTMLを追加するメソッドをノックアウトコード$( "#knockoutContainerDiv")。append( "knockout binding code with nokout binding code")で呼び出し、applyBindingを再度呼び出します
aamir sajjad

1
こんにちはammir、私はあなたとほとんど同じシナリオを持っていました。私がko.cleanNode(element);を使用しなければならなかったという事実を除いて、あなたが言及したことはすべてうまくいきました。すべての再バインドの前。
ラドスラフミンチェフ2014

@RadoslavMinchev私はあなたをさらに助けることができると思いますか、そうであればどのようにしたら、私は特定の質問についての私の考え/経験を共有させていただきます。
aamir sajjad 14

どうも。@aamirsajjad、ちょうど私のために働いたのはそれを機能させるためにcleanNode()関数を呼び出すことであると述べたかっただけです。
ラドスラフミンチェフ2014

6

KOの内部関数を使用してJQueryの包括的なイベントハンドラーの削除を処理する代わりに、withまたはtemplateバインディングを使用する方がはるかに優れています。これを行うと、koはDOMのその部分を再作成するため、自動的にクリーンアップされます。これも推奨される方法です。https//stackoverflow.com/a/15069509/207661を参照してください


4

バインディングを常に維持し、それに関連付けられたデータを更新する方が良いと思います。この問題に遭遇し、.resetAll()、データを保持している配列メソッドが、これを行う最も効果的な方法であることが。

基本的には、ViewModelを介してレンダリングされるデータを含むいくつかのグローバル変数から始めることができます。

var myLiveData = ko.observableArray();

myLiveData通常の配列を作成できないことに気づくのにしばらく時間がかかりました-ko.oberservableArray部分が重要でした。

その後、先に進み、やりたいことをすべて実行できますmyLiveData。たとえば、次のように$.getJSON呼び出します。

$.getJSON("http://foo.bar/data.json?callback=?", function(data) {
    myLiveData.removeAll();
    /* parse the JSON data however you want, get it into myLiveData, as below */
    myLiveData.push(data[0].foo);
    myLiveData.push(data[4].bar);
});

これが完了したら、通常どおりViewModelを使用してバインディングを適用できます。

function MyViewModel() {
    var self = this;
    self.myData = myLiveData;
};
ko.applyBindings(new MyViewModel());

次に、HTMLで使用するだけ myData通常どおりにします。

このようにして、どちらの関数からでもmyLiveDataを変更できます。たとえば、数秒ごとに更新したい場合は、その$.getJSON行を関数にラップして呼び出しますsetInterval。保持することを覚えている限り、バインディングを削除する必要はありません。myLiveData.removeAll();行。

データが非常に大きい場合を除いて、ユーザーはアレイをリセットしてから最新のデータを追加して戻すまでの時間に気付くことさえありません。


質問を投稿してからしばらくして、これが私が今やっていることです。これらのKnockoutメソッドの一部が文書化されていないか、実際に調べて(関数名を知っている)必要があることだけです。
awj 2013年

私もこれを見つけるのが非常に困難でした(ドキュメントを掘り下げる時間数が結果のコード行数を超えたとき...わあ)。うまくいきました。
ザック

2

最近、メモリリークの問題が発生ko.cleanNode(element);しましたが、私には実行できませんko.removeNode(element);でした。JavaScript + Knockout.jsのメモリリーク-オブジェクトが破棄されていることを確認する方法


ノックアウト3.1では、ko.removeNodeが実際にko.cleanNodeを呼び出します。しかし、それが以前のバージョンに当てはまったことを知りません。
kiprainey 2014年

1

これについて考えましたか?

try {
    ko.applyBindings(PersonListViewModel);
}
catch (err) {
    console.log(err.message);
}

Knockoutでこのコードを見つけたので、これを思いつきました

    var alreadyBound = ko.utils.domData.get(node, boundElementDomDataKey);
    if (!sourceBindings) {
        if (alreadyBound) {
            throw Error("You cannot apply bindings multiple times to the same element.");
        }
        ko.utils.domData.set(node, boundElementDomDataKey, true);
    }

だから私にとっては、それはすでにバインドされている問題ではなく、エラーがキャッチされて処理されなかったということです...


0

ビューモデルに多くのdivバインディングが含まれている場合、それをクリアする最善の方法ko.applyBindings(new someModelView);は、ko.cleanNode($("body")[0]);これを使用することです。これによりko.applyBindings(new someModelView2);、以前のビューモデルがまだバインドされているという心配なしに、新しいを動的に呼び出すことができます。


4
追加したい点がいくつかあります。(1)これにより、Webページからすべてのバインディングがクリアされます。これはアプリケーションに適している可能性がありますが、ページの複数の部分に個別にバインディングが追加されているアプリケーションはたくさんあると思います理由。単一のスイープコマンドですべてのバインディングをクリーンアップすることは、多くのユーザーにとって役に立ちません。(2)より速く、より効率的で、ネイティブなJavaScriptの取得方法は$("body")[0]ですdocument.body
awj 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.