it-swarm.com.de

Wie kann man beobachtbare Bindungen in Knockout.js löschen/entfernen?

Ich baue Funktionalität auf einer Webseite auf, die der Benutzer mehrmals ausführen kann. Durch die Aktion des Benutzers wird ein Objekt/Modell erstellt und mithilfe von ko.applyBindings () auf HTML angewendet.

Das datengebundene HTML wird über jQuery-Vorlagen erstellt.

So weit, ist es gut.

Wenn ich diesen Schritt durch das Erstellen eines zweiten Objekts/Modells wiederhole und ko.applyBindings () aufrufe, treten zwei Probleme auf:

  1. Das Markup zeigt das vorherige Objekt/Modell sowie das neue Objekt/Modell.
  2. Ein Javascript-Fehler tritt in Bezug auf eine der Eigenschaften im Objekt/Modell auf, obwohl er immer noch im Markup dargestellt wird.

Um dieses Problem zu umgehen, rufe ich nach dem ersten Durchlauf das .empty () von jQuery auf, um die HTML-Vorlage zu entfernen, die alle Datenbindungsattribute enthält, sodass sie nicht mehr im DOM enthalten ist. Wenn der Benutzer den Prozess für den zweiten Durchlauf startet, wird der datengebundene HTML-Code erneut zum DOM hinzugefügt.

Aber wie gesagt, wenn der HTML-Code erneut zum DOM hinzugefügt und an das neue Objekt/Modell gebunden wird, enthält er immer noch Daten vom ersten Objekt/Modell, und ich bekomme immer noch den JS-Fehler, der nicht auftritt beim ersten Durchgang.

Die Schlussfolgerung scheint zu sein, dass Knockout diese gebundenen Eigenschaften beibehält, obwohl das Markup aus dem DOM entfernt wird.

Was ich also suche, ist ein Mittel, um diese gebundenen Eigenschaften von Knockout zu entfernen; Knockout zu sagen, dass es kein beobachtbares Modell mehr gibt. Gibt es eine Möglichkeit, dies zu tun?

EDIT

Der grundlegende Prozess ist, dass der Benutzer eine Datei hochlädt; Der Server antwortet dann mit einem JSON-Objekt, der datengebundene HTML-Code wird zum DOM hinzugefügt, und das JSON-Objektmodell wird mit diesem HTML-Code gebunden

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

Nachdem der Benutzer eine Auswahl für das Modell getroffen hat, wird dasselbe Objekt wieder auf dem Server bereitgestellt, der datengebundene HTML-Code wird aus DOM entfernt, und ich habe die folgende JS

mn.AccountCreationModel = null;

Wenn der Benutzer dies noch einmal tun möchte, werden alle diese Schritte wiederholt.

Ich befürchte, der Code ist zu "involviert", um eine jsFiddle-Demo zu erstellen.

111
awj

Haben Sie versucht, die Clean-Node-Methode von Knockout für Ihr DOM-Element aufzurufen, um die im Speicher gebundenen Objekte zu beseitigen?

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

Durch das erneute Anwenden der Knockout-Bindungen auf genau dieses Element mit Ihren neuen Ansichtsmodellen wird Ihre Ansichtsbindung aktualisiert.

165
KodeKreachor

Für ein Projekt, an dem ich arbeite, habe ich eine einfache ko.unapplyBindings-Funktion geschrieben, die einen jQuery-Knoten und das boolean remove akzeptiert. Zunächst werden alle jQuery-Ereignisse aufgehoben, da die ko.cleanNode-Methode dies nicht berücksichtigt. Ich habe auf Speicherlecks getestet und es scheint gut zu funktionieren.

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

Sie können versuchen, die von knockout angebotene Bindung mit zu verwenden: http://knockoutjs.com/documentation/with-binding.html Die Idee ist, Bindungen einmal anzuwenden, und wann immer sich Ihre Daten ändern, aktualisieren Sie sie einfach Ihr Modell.

Nehmen wir an, Sie haben ein Top-Level-Ansichtsmodell storeViewModel, Ihren Warenkorb, der durch cartViewModel dargestellt wird, und eine Liste der Artikel in diesem Warenkorb - sagen wir cartItemsViewModel.

Sie würden das Modell der obersten Ebene - das storeViewModel - an die gesamte Seite binden. Anschließend können Sie die Teile Ihrer Seite trennen, die für den Warenkorb oder die Warenkorbelemente verantwortlich sind.

Nehmen wir an, dass das cartItemsViewModel die folgende Struktur hat:

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

Das cartItemsViewModel kann zu Beginn leer sein.

Die Schritte würden so aussehen:

  1. Bindungen in HTML definieren. Trennen Sie die cartItemsViewModel-Bindung.

    
        <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. Das Geschäftsmodell stammt von Ihrem Server (oder wird auf andere Weise erstellt).

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. Definieren Sie leere Modelle in Ihrem Ansichtsmodell der obersten Ebene. Dann kann eine Struktur dieses Modells mit tatsächlichen Daten aktualisiert werden.

    
        storeViewModel.cartItemsViewModel = ko.observable();
        storeViewModel.cartViewModel = ko.observable();
     
  4. Binden Sie das Ansichtsmodell der obersten Ebene.

    ko.applyBindings(storeViewModel);

  5. Wenn das cartItemsViewModel-Objekt verfügbar ist, weisen Sie es dem zuvor definierten Platzhalter zu.

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

Wenn Sie die Einkaufswagenartikel löschen möchten: storeViewModel.cartItemsViewModel(null);

Knockout kümmert sich um HTML - d. H. Es wird angezeigt, wenn das Modell nicht leer ist und der Inhalt von div (der mit "mit Bindung") verschwindet.

12

Ich muss ko.applyBinding bei jedem Klick auf die Schaltfläche "Suchen" aufrufen, und die gefilterten Daten werden vom Server zurückgegeben.

Ich habe erfahren, wenn wir foreach durch template ersetzen, sollte es bei Sammlungen/ObservableArray gut funktionieren.

Sie finden dieses Szenario möglicherweise hilfreich.

<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

Anstatt die internen Funktionen von KO zu verwenden und sich mit dem Entfernen des JQuery-Ereignishandlers zu befassen, ist die Verwendung von Bindungen with oder template eine viel bessere Idee. Wenn Sie dies tun, erstellt ko diesen Teil von DOM neu und es wird automatisch gesäubert. Dies wird auch empfohlen, siehe hier: https://stackoverflow.com/a/15069509/207661 .

6
Shital Shah

Ich denke, es ist vielleicht besser, die Bindung für die gesamte Zeit beizubehalten und die damit verbundenen Daten einfach zu aktualisieren. Ich bin auf dieses Problem gestoßen und habe festgestellt, dass das Aufrufen mit der .resetAll()-Methode für das Array, in dem ich meine Daten aufbewahrte, der effektivste Weg ist, dies zu tun. 

Grundsätzlich können Sie mit einem globalen var beginnen, das Daten enthält, die über ViewModel gerendert werden sollen:

var myLiveData = ko.observableArray();

Es dauerte eine Weile, bis mir klar wurde, dass ich myLiveData nicht zu einem normalen Array machen konnte - der ko.oberservableArray-Teil war wichtig. 

Dann können Sie fortfahren und machen, was Sie wollen, myLiveData. Führen Sie beispielsweise einen $.getJSON-Aufruf aus:

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

Wenn Sie dies getan haben, können Sie wie üblich Bindungen mit Ihrem ViewModel anwenden:

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

Dann verwenden Sie im HTML-Format wie gewohnt myData

Auf diese Weise können Sie mit myLiveData von jeder beliebigen Funktion aus einen Muck machen. Wenn Sie beispielsweise alle paar Sekunden ein Update durchführen möchten, wickeln Sie einfach die $.getJSON-Zeile in eine Funktion ein und rufen Sie setInterval auf. Sie müssen die Bindung nie entfernen, solange Sie daran denken, die myLiveData.removeAll();-Zeile beizubehalten. 

Wenn Ihre Daten nicht wirklich groß sind, können Benutzer die Zeit zwischen dem Zurücksetzen des Arrays und dem Hinzufügen der aktuellsten Daten nicht einmal bemerken. 

4
Zac

Ich hatte kürzlich ein Problem mit dem Speicherleck und ko.cleanNode(element); würde es nicht für mich tun --ko.removeNode(element);. Javascript + Knockout.js Speicherverlust - Wie stellen Sie sicher, dass das Objekt zerstört wird?

2

Haben Sie darüber nachgedacht:

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

Ich kam dazu, weil ich in Knockout diesen Code gefunden habe

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

Für mich ist es nicht wirklich ein Problem, dass es bereits gebunden ist, sondern dass der Fehler nicht aufgefangen und behandelt wurde ...

1
ozzy432836

Wenn das Ansichtsmodell viele div-Bindungen enthält, kann ich die Funktion ko.applyBindings(new someModelView); am besten löschen: ko.cleanNode($("body")[0]); Auf diese Weise können Sie eine neue ko.applyBindings(new someModelView2); dynamisch aufrufen, ohne dass das vorherige Ansichtsmodell noch gebunden wird. 

0
Derrick Hampton