Ich habe drei verschiedene Möglichkeiten, eine Ansicht und ihre Unteransichten zu initialisieren und zu rendern, und jede hat unterschiedliche Probleme. Ich bin gespannt, ob es einen besseren Weg gibt, um alle Probleme zu lösen:
Szenario Eins:
Initialisieren Sie die untergeordneten Elemente in der Initialisierungsfunktion des Elternteils. Auf diese Weise bleibt nicht alles beim Rendern hängen, sodass das Rendern weniger blockiert wird.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.render().appendTo(this.$('.container-placeholder');
}
Die Probleme:
Das größte Problem besteht darin, dass beim zweiten Aufruf von render auf dem übergeordneten Element alle untergeordneten Ereignisbindungen entfernt werden. (Dies liegt daran, wie jQuery
$.html()
funktioniert.) Dies könnte durch einen Anruf gemildert werden.this.child.delegateEvents().render().appendTo(this.$el);
Im ersten und häufigsten Fall erledigen Sie jedoch unnötig mehr Arbeit.Durch Anhängen der untergeordneten Elemente erzwingen Sie, dass die Renderfunktion die DOM-Struktur der Eltern kennt, damit Sie die gewünschte Reihenfolge erhalten. Das bedeutet, dass zum Ändern einer Vorlage möglicherweise die Renderfunktion einer Ansicht aktualisiert werden muss.
Szenario Zwei:
Initialisieren Sie die Kinder im initialize()
Standbild des Elternteils , aber anstatt sie anzuhängen, verwenden Sie sie setElement().delegateEvents()
, um das Kind auf ein Element in der Elternvorlage zu setzen.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}
Probleme:
- Dies macht das
delegateEvents()
Jetzt notwendig, was ein wenig negativ ist, da es nur bei nachfolgenden Aufrufen im ersten Szenario notwendig ist.
Szenario drei:
Initialisieren Sie render()
stattdessen die untergeordneten Elemente in der übergeordneten Methode.
initialize : function () {
//parent init stuff
},
render : function () {
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
Probleme:
Dies bedeutet, dass die Renderfunktion nun auch mit der gesamten Initialisierungslogik verknüpft werden muss.
Wenn ich den Status einer der untergeordneten Ansichten bearbeite und dann Render für das übergeordnete Element aufrufe, wird ein vollständig neues untergeordnetes Element erstellt und der gesamte aktuelle Status geht verloren. Das scheint auch für Speicherlecks heikel zu werden.
Wirklich neugierig darauf, dass eure Jungs das angehen. Welches Szenario würden Sie verwenden? oder gibt es eine vierte magische, die all diese Probleme löst?
Haben Sie jemals einen gerenderten Status für eine Ansicht verfolgt? Eine renderedBefore
Flagge sagen ? Scheint wirklich nervös.
quelle
delegateEvents()
nach dem angerufen werdensetElement()
? Gemäß den folgenden Dokumenten: "... und die delegierten Ereignisse der Ansicht vom alten auf das neue Element verschieben" sollte diesetElement
Methode selbst die erneute Delegierung von Ereignissen behandeln.Antworten:
Das ist eine gute Frage. Backbone ist großartig, weil es keine Annahmen macht, aber es bedeutet, dass Sie solche Dinge selbst implementieren müssen. Nachdem ich meine eigenen Sachen durchgesehen habe, stelle ich fest, dass ich (irgendwie) eine Mischung aus Szenario 1 und Szenario 2 verwende. Ich glaube nicht, dass ein viertes magisches Szenario existiert, weil einfach alles, was Sie in Szenario 1 und 2 tun, sein muss getan.
Ich denke, es wäre am einfachsten zu erklären, wie ich mit einem Beispiel umgehen möchte. Angenommen, ich habe diese einfache Seite in die angegebenen Ansichten unterteilt:
Angenommen, der HTML-Code ist nach dem Rendern ungefähr so:
Hoffentlich ist es ziemlich offensichtlich, wie der HTML-Code mit dem Diagramm übereinstimmt.
Das
ParentView
hält 2 Kind Ansichten,InfoView
undPhoneListView
sowie ein paar zusätzliche divs, von denen eines ,#name
muss an einem bestimmten Punkt eingestellt werden.PhoneListView
Enthält eigene untergeordnete Ansichten, eine Reihe vonPhoneView
Einträgen.Also weiter zu Ihrer eigentlichen Frage. Ich gehe mit der Initialisierung und dem Rendern je nach Ansichtstyp unterschiedlich um. Ich teile meine Ansichten in zwei Typen ein,
Parent
Ansichten undChild
Ansichten.Der Unterschied zwischen ihnen ist einfach:
Parent
Ansichten enthalten untergeordnete Ansichten,Child
Ansichten jedoch nicht. Also in meinem BeispielParentView
undPhoneListView
sindParent
Ansichten, währendInfoView
und diePhoneView
EinträgeChild
Ansichten sind.Wie ich bereits erwähnt habe, besteht der größte Unterschied zwischen diesen beiden Kategorien darin, wann sie rendern dürfen. In einer perfekten Welt möchte ich, dass
Parent
Ansichten immer nur einmal gerendert werden. Es liegt an ihren untergeordneten Ansichten, das erneute Rendern durchzuführen, wenn sich die Modelle ändern.Child
Ansichten hingegen erlaube ich, sie jederzeit neu zu rendern, da sie keine anderen Ansichten haben, die sich auf sie stützen.Für
Parent
Ansichten möchte ich, dass meineinitialize
Funktionen einige Dinge tun:InfoView
würde zugewiesen werden#info
).Schritt 1 ist ziemlich selbsterklärend.
Schritt 2, das Rendern, wird ausgeführt, sodass alle Elemente, auf die sich die untergeordneten Ansichten stützen, bereits vorhanden sind, bevor ich versuche, sie zuzuweisen. Auf diese Weise weiß ich, dass alle Kinder
events
richtig eingestellt sind, und ich kann ihre Blöcke so oft neu rendern, wie ich möchte, ohne mir Sorgen machen zu müssen, dass etwas neu delegiert werden muss. Ich habe hier eigentlichrender
keine kindlichen Ansichten, ich erlaube ihnen, das in ihren eigenen zu tuninitialization
.Die Schritte 3 und 4 werden tatsächlich zur gleichen Zeit ausgeführt, zu der ich
el
beim Erstellen der untergeordneten Ansicht übergebe. Ich möchte hier ein Element übergeben, da ich der Meinung bin, dass die Eltern bestimmen sollten, wo das Kind nach seiner eigenen Ansicht seinen Inhalt ablegen darf.Beim Rendern versuche ich, es für
Parent
Ansichten ziemlich einfach zu halten . Ich möchte, dass dierender
Funktion nur die übergeordnete Ansicht rendert. Keine Ereignisdelegation, kein Rendern von untergeordneten Ansichten, nichts. Nur ein einfaches Rendern.Manchmal funktioniert das aber nicht immer. In meinem obigen Beispiel muss das
#name
Element beispielsweise jedes Mal aktualisiert werden, wenn sich der Name im Modell ändert. Dieser Block ist jedoch Teil derParentView
Vorlage und wird nicht von einer dediziertenChild
Ansicht verarbeitet. Deshalb arbeite ich daran. Ich werde eine ArtsubRender
Funktion erstellen , die nur den Inhalt des#name
Elements ersetzt und nicht das gesamte#parent
Element in den Papierkorb werfen muss. Dies mag wie ein Hack erscheinen, aber ich habe wirklich festgestellt, dass es besser funktioniert, als sich Gedanken über das erneute Rendern des gesamten DOM und das erneute Anbringen von Elementen und dergleichen machen zu müssen. Wenn ich es wirklich sauber machen wollte, würde ich eine neueChild
Ansicht (ähnlich derInfoView
) erstellen , die den#name
Block handhaben würde .Bei
Child
Ansichten ist die Ansichtinitialization
ziemlich ähnlichParent
, nur ohne dass weitereChild
Ansichten erstellt werden müssen. So:Child
Das Rendern von Ansichten ist ebenfalls sehr einfach. Rendern Sie einfach den Inhalt von my und legen Sie ihn festel
. Wieder kein Durcheinander mit der Delegation oder so etwas.Hier ist ein Beispielcode, wie mein
ParentView
aussehen könnte:Sie können meine Implementierung
subRender
hier sehen. Dadurch , dass Änderungen gebundensubRender
stattrender
, habe ich nicht zu befürchten Strahlen weg und den Wiederaufbau des gesamten Blocks.Hier ist ein Beispielcode für den
InfoView
Block:Die Bindungen sind hier der wichtige Teil. Durch die Bindung an mein Modell muss ich mich nie mehr darum kümmern,
render
mich selbst manuell anzurufen. Wenn sich das Modell ändert, wird dieser Block selbst neu gerendert, ohne andere Ansichten zu beeinflussen.Das
PhoneListView
wird dem ähnlich seinParentView
, Sie benötigen nur ein wenig mehr Logik sowohl in Ihrerinitialization
als auch in Ihrenrender
Funktionen, um Sammlungen zu verwalten. Wie Sie mit der Sammlung umgehen, liegt ganz bei Ihnen, aber Sie müssen zumindest die Sammlungsereignisse abhören und entscheiden, wie Sie rendern möchten (Anhängen / Entfernen oder einfach das Rendern des gesamten Blocks). Ich persönlich möchte neue Ansichten anhängen und alte entfernen, nicht die gesamte Ansicht neu rendern.Das
PhoneView
wird fast identisch mit dem seinInfoView
, nur das Hören der Modelländerungen, die es interessiert.Hoffentlich hat dies ein wenig geholfen. Bitte lassen Sie mich wissen, wenn etwas verwirrend oder nicht detailliert genug ist.
quelle
render
innerhalb derinitialize
Methode eine schlechte Praxis ist, da es Sie daran hindert, in Fällen, in denen Sie nicht sofort rendern möchten, leistungsfähiger zu sein. Was denkst du darüber?PhoneListView
?Ich bin nicht sicher, ob dies Ihre Frage direkt beantwortet, aber ich denke, es ist relevant:
http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
Der Kontext, in dem ich diesen Artikel erstellt habe, ist natürlich unterschiedlich, aber ich denke, die beiden von mir angebotenen Lösungen sowie die Vor- und Nachteile der einzelnen Lösungen sollten Sie in die richtige Richtung bewegen.
quelle
Für mich scheint es nicht die schlechteste Idee der Welt zu sein, zwischen dem Intital-Setup und den nachfolgenden Setups Ihrer Ansichten über eine Art Flag zu unterscheiden. Um dies sauber und einfach zu machen, sollte das Flag zu Ihrer eigenen Ansicht hinzugefügt werden, wodurch die Backbone-Ansicht (Basisansicht) erweitert werden soll.
Genau wie Derick bin ich mir nicht ganz sicher, ob dies Ihre Frage direkt beantwortet, aber ich denke, dass es in diesem Zusammenhang zumindest erwähnenswert sein könnte.
quelle
Kevin Peel gibt eine großartige Antwort - hier ist meine tl; dr-Version:
quelle
Ich versuche zu vermeiden, dass solche Ansichten gekoppelt werden. Normalerweise mache ich zwei Möglichkeiten:
Verwenden Sie einen Router
Grundsätzlich lassen Sie Ihre Router-Funktion die übergeordnete und untergeordnete Ansicht initialisieren. Die Ansicht kennt sich also nicht, aber der Router kümmert sich um alles.
Übergabe des gleichen El an beide Ansichten
Beide kennen dasselbe DOM und können es beliebig bestellen.
quelle
Ich gebe jedem Kind eine Identität (was Backbone bereits für Sie getan hat: cid)
Wenn Container das Rendern durchführt, wird mithilfe von 'cid' und 'tagName' ein Platzhalter für jedes Kind generiert, sodass die Kinder in der Vorlage keine Ahnung haben, wo es vom Container abgelegt wird.
als Sie verwenden können
Es wird kein angegebener Platzhalter benötigt, und Container generiert nur den Platzhalter und nicht die DOM-Struktur der Kinder. Cotainer und Children generieren immer noch und nur einmal eigene DOM-Elemente.
quelle
Hier ist ein leichtes Mixin zum Erstellen und Rendern von Unteransichten, das meiner Meinung nach alle Probleme in diesem Thread behebt:
https://github.com/rotundasoftware/backbone.subviews
Der Ansatz dieses Plugins besteht darin, Unteransichten nach dem ersten Rendern der übergeordneten Ansicht zu erstellen und zu rendern . Beim späteren Rendern der übergeordneten Ansicht $ .trennen Sie die Unteransichtselemente, rendern Sie das übergeordnete Element erneut, fügen Sie die Unteransichtselemente an den entsprechenden Stellen ein und rendern Sie sie erneut. Auf diese Weise werden Unteransichten von Objekten beim nachfolgenden Rendern wiederverwendet, und Ereignisse müssen nicht erneut delegiert werden.
Beachten Sie, dass der Fall einer Sammlungsansicht (bei der jedes Modell in der Sammlung mit einer Unteransicht dargestellt wird) ganz anders ist und meiner Meinung nach eine eigene Diskussion / Lösung verdient. Die beste allgemeine Lösung, die mir für diesen Fall bekannt ist, ist die CollectionView in Marionette .
BEARBEITEN: Für den Fall der Sammlungsansicht möchten Sie möglicherweise auch diese stärker auf die Benutzeroberfläche ausgerichtete Implementierung überprüfen , wenn Sie eine Auswahl von Modellen basierend auf Klicks und / oder Ziehen und Ablegen zur Neuordnung benötigen.
quelle