web-dev-qa-db-de.com

Ansichts- und Modellobjekte in Backbone.js ablegen

Welche Methode ist am effizientesten, um Modell-/Ansichtsinstanzen zu entsorgen, wenn sie nicht benötigt werden?

Normalerweise stecke ich die gesamte Logik in den Controller/Router. Es ist derjenige, der entscheidet, welche Ansichten erstellt werden sollen und welche Modelle an sie geliefert werden sollen. In der Regel gibt es einige Handlerfunktionen, die verschiedenen Benutzeraktionen oder Routen entsprechen. Dabei erstelle ich jedes Mal neue Ansichtsinstanzen, wenn ein Handler ausgeführt wird. Das sollte natürlich alles beseitigen, was ich zuvor in der Ansichtsinstanz gespeichert habe. Es gibt jedoch Situationen, in denen einige Ansichten die DOM-Ereignishandler selbst beibehalten und die Bindung nicht ordnungsgemäß aufgehoben wird, was dazu führt, dass diese Instanzen am Leben bleiben. Ich wünschte, es gäbe einen geeigneten Weg, um View-Instanzen zu zerstören, wenn zum Beispiel ihr el (DOM-Repräsentation) abgetrennt oder aus dem DOM geworfen wird

64
user802232

sie sind auf dem richtigen Weg. Sie sollten ein Objekt haben, das den Lebenszyklus Ihrer Ansichten steuert. Ich mag das nicht aus meiner Sicht. Ich mag es, ein separates Objekt dafür zu erstellen.

das, was Sie tun müssen, ist, die Ereignisse bei Bedarf zu lösen. Zu diesem Zweck empfiehlt es sich, für alle Ansichten eine "close" -Methode zu erstellen und mit dem Objekt, das den Lebenszyklus aller Elemente steuert, immer die close-Methode aufzurufen.

beispielsweise:


  function AppController(){
    this.showView = function (view){
      if (this.currentView){
        this.currentView.close();
      }
      this.currentView = view;
      this.currentView.render();
      $("#someElement").html(this.currentView.el);
    }
  }

an diesem Punkt würden Sie Ihren Code so einrichten, dass nur eine Instanz des AppControllers vorhanden ist, und Sie würden immer appController.showView(...) von Ihrem Router oder einem anderen Code aufrufen, der eine Ansicht im #someElement Teil Ihres Bildschirms.

(Ich habe ein weiteres Beispiel für eine sehr einfache Backbone-App, die eine "AppView" (eine Backbone-Ansicht, die den Hauptteil der App ausführt) verwendet, hier: http://jsfiddle.net/derickbailey/dHrXv/9) / )

die Methode close ist in Ansichten standardmäßig nicht vorhanden. Sie müssen daher für jede Ihrer Ansichten eine eigene Methode erstellen. Es gibt zwei Dinge, die immer in der Methode close sein sollten: this.unbind() und this.remove(). Darüber hinaus sollten Sie, wenn Sie Ihre Ansicht an Modell- oder Sammlungsereignisse binden, deren Bindung in der close-Methode aufheben.

beispielsweise:


  MyView = Backbone.View.extend({
    initialize: function(){
      this.model.bind("change", this.modelChanged, this);
    },

    modelChanged: function(){
      // ... do stuff here
    },

    close: function(){
      this.remove();
      this.unbind();
      this.model.unbind("change", this.modelChanged);
    }
  });

dadurch werden alle Ereignisse aus dem DOM (über this.remove()), alle Ereignisse, die die Ansicht selbst auslösen kann (über this.unbind()) und das Ereignis, an das die Ansicht gebunden ist, ordnungsgemäß bereinigt aus dem Modell (über this.model.unbind(...)).

77
Derick Bailey

Eine einfachere Möglichkeit ist das Hinzufügen einer benutzerdefinierten Methode zum Schließen des Backbone.View-Objekts

Backbone.View.prototype.close = function () {
  this.$el.empty();
  this.unbind();
};

Mit dem obigen Code können Sie Folgendes tun

var myView = new MyView();

myView.close();

kinderleicht.

13
Shaheen Ghiassy

Ich kenne immer die Ansichten und verwende die Modelle manchmal wieder. Das Aufheben der Zuordnung der Ansichten kann ein Problem sein, wenn Sie die Modelle beibehalten. Die Modelle behalten möglicherweise einen Verweis auf die Ansicht bei, wenn sie nicht ordnungsgemäß ungebunden sind.

Ab Backbone ~ 0.9.9 ermöglicht das Binden von Modellen mit view.listenTo () anstelle von model.on () eine einfachere Bereinigung durch Invertierung der Steuerung (Ansichten steuern Bindungen im Gegensatz zu Modellen). Wenn view.listenTo () zum Binden verwendet wird, werden durch einen Aufruf von view.stopListening () oder view.remove () alle Bindungen entfernt. Ähnlich wie beim Aufruf von model.off (null, null, this).

Ich möchte Ansichten bereinigen, indem ich die Ansicht mit einer Schließfunktion erweitere, die Teilansichten halbautomatisch aufruft. Auf die Unteransichten muss durch die Eigenschaften des übergeordneten Elements verwiesen werden, oder sie müssen einem Array innerhalb des übergeordneten Elements mit dem Namen childViews [] hinzugefügt werden.

Hier ist die Close-Funktion, die ich benutze ..

// fired by the router, signals the destruct event within top view and 
// recursively collapses all the sub-views that are stored as properties
Backbone.View.prototype.close = function () {

    // calls views closing event handler first, if implemented (optional)
    if (this.closing) {
        this.closing();  // this for custom cleanup purposes
    }

    // first loop through childViews[] if defined, in collection views
    //  populate an array property i.e. this.childViews[] = new ControlViews()
    if (this.childViews) {
        _.each(this.childViews, function (child) {
            child.close();
        });
    }

    // close all child views that are referenced by property, in model views
    //  add a property for reference i.e. this.toolbar = new ToolbarView();
    for (var prop in this) {
        if (this[prop] instanceof Backbone.View) {
            this[prop].close();
        }
    }

    this.unbind();
    this.remove();

    // available in Backbone 0.9.9 + when using view.listenTo, 
    //  removes model and collection bindings
    // this.stopListening(); // its automatically called by remove()

    // remove any model bindings to this view 
    //  (pre Backbone 0.9.9 or if using model.on to bind events)
    // if (this.model) {
    //  this.model.off(null, null, this);
    // }

    // remove and collection bindings to this view 
    //  (pre Backbone 0.9.9 or if using collection.on to bind events)
    // if (this.collection) {
    //  this.collection.off(null, null, this);
    // }
}

Dann wird eine Ansicht wie folgt deklariert.

views.TeamView = Backbone.View.extend({

    initialize: function () {
        // instantiate this array to ensure sub-view destruction on close()
        this.childViews = [];  

        this.listenTo(this.collection, "add", this.add);
        this.listenTo(this.collection, "reset", this.reset);

        // storing sub-view as a property will ensure destruction on close()
        this.editView = new views.EditView({ model: this.model.edits });
        $('#edit', this.el).html(this.editView.render().el);
    },

    add: function (member) {
        var memberView = new views.MemberView({ model: member });
        this.childViews.Push(memberView);    // add child to array

        var item = memberView.render().el;
        this.$el.append(item);
    },

    reset: function () {
        // manually purge child views upon reset
        _.each(this.childViews, function (child) {
            child.close();
        });

        this.childViews = [];
    },

    // render is called externally and should handle case where collection
    // was already populated, as is the case if it is recycled
    render: function () {
        this.$el.empty();

        _.each(this.collection.models, function (member) {
            this.add(member);
        }, this);
        return this;
    }

    // fired by a prototype extension
    closing: function () {
        // handle other unbinding needs, here
    }
});
6
djabraham