web-dev-qa-db-ja.com

Knockout.jsで監視可能なバインディングをクリア/削除する方法は?

ユーザーが複数回実行できる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デモを実行するには「関与しすぎ」ていると思います。

112
awj

DOM要素でノックアウトのクリーンノードメソッドを呼び出して、メモリ内のバインドされたオブジェクトを破棄しようとしましたか?

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

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

168
KodeKreachor

私が取り組んでいるプロジェクトでは、jQueryノードとremoveブール値を受け入れる簡単なko.unapplyBindings関数を作成しました。 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]);
    }
};
31

ノックアウトが提供するwithバインディングを使用してみてください: http://knockoutjs.com/documentation/with-binding.html バインディングを1回、データが変更されるたびに、モデルを更新するだけです。

最上位ビューモデル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(「with binding」を持つもの)の内容が消えると表示されます。

12

検索ボタンがクリックされるたびに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>
9
aamir sajjad

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

6
Shital Shah

バインディングを常に保持し、それに関連付けられたデータを更新する方が良いと思います。私はこの問題にぶつかり、データを保持していた配列で.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();行を保持することを覚えている限り、バインディングを削除する必要はありません。

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

4
Zac

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

2

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

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

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

1
ozzy432836