J'ai trois façons différentes d'initialiser et de rendre une vue et ses sous-vues, et chacune d'elles a des problèmes différents. Je suis curieux de savoir s'il existe une meilleure façon de résoudre tous les problèmes:
Scénario un:
Initialisez les enfants dans la fonction d'initialisation du parent. De cette façon, tout ne reste pas bloqué dans le rendu afin qu'il y ait moins de blocage sur le rendu.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.render().appendTo(this.$('.container-placeholder');
}
Les problèmes:
Le plus gros problème est que l'appel de rendu sur le parent pour la deuxième fois supprimera toutes les liaisons d'événements enfant. (C'est à cause du fonctionnement de jQuery
$.html()
.) Cela pourrait être atténué en appelant à lathis.child.delegateEvents().render().appendTo(this.$el);
place, mais le premier, et le plus souvent, vous faites plus de travail inutilement.En ajoutant les enfants, vous forcez la fonction de rendu à avoir connaissance de la structure DOM des parents afin d'obtenir l'ordre que vous souhaitez. Ce qui signifie que la modification d'un modèle peut nécessiter la mise à jour de la fonction de rendu d'une vue.
Scénario deux:
Initialisez les enfants dans l' initialize()
alambic du parent , mais au lieu de les ajouter, utilisez setElement().delegateEvents()
pour définir l'enfant sur un élément du modèle de parents.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}
Problèmes:
- Cela rend le
delegateEvents()
nécessaire maintenant, ce qui est légèrement négatif par rapport au fait qu'il n'est nécessaire que lors des appels suivants dans le premier scénario.
Troisième scénario:
Initialisez render()
plutôt les enfants dans la méthode du parent .
initialize : function () {
//parent init stuff
},
render : function () {
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
Problèmes:
Cela signifie que la fonction de rendu doit désormais également être liée à toute la logique d'initialisation.
Si je modifie l'état de l'une des vues enfant, puis que j'appelle render sur le parent, un tout nouvel enfant sera créé et tout son état actuel sera perdu. Ce qui semble également être risqué pour les fuites de mémoire.
Vraiment curieux de savoir ce que vos gars en pensent. Quel scénario utiliseriez-vous? ou y en a-t-il un quatrième magique qui résout tous ces problèmes?
Avez-vous déjà suivi l'état d'un rendu pour une vue? Dites un renderedBefore
drapeau? Semble vraiment dégingandé.
la source
delegateEvents()
après lesetElement()
? Selon les documents: "... et déplacer les événements délégués de la vue de l'ancien élément vers le nouveau", lasetElement
méthode elle-même doit gérer la redélégation des événements.Réponses:
c'est une excellente question. L'épine dorsale est excellente en raison du manque d'hypothèses qu'elle fait, mais cela signifie que vous devez (décider comment) mettre en œuvre des choses comme ça vous-même. Après avoir regardé à travers mes propres trucs, je trouve que j'utilise (en quelque sorte) un mélange de scénario 1 et de scénario 2. Je ne pense pas qu'un quatrième scénario magique existe parce que, tout simplement, tout ce que vous faites dans les scénarios 1 et 2 doit être terminé.
Je pense qu'il serait plus facile d'expliquer comment j'aime le gérer avec un exemple. Supposons que cette simple page soit divisée en vues spécifiées:
Disons que le HTML est, après avoir été rendu, quelque chose comme ceci:
J'espère que la façon dont le code HTML correspond au diagramme est assez évidente.
Les
ParentView
détient 2 vues de l' enfant,InfoView
etPhoneListView
ainsi que quelques divs supplémentaires, dont l' une#name
, doit être fixé à un moment donné.PhoneListView
contient ses propres vues enfants, un tableau d'PhoneView
entrées.Passons donc à votre question réelle. Je gère l'initialisation et le rendu différemment en fonction du type de vue. Je divise mes vues en deux types,
Parent
vues etChild
vues.La différence entre les deux est simple, les
Parent
vues contiennent des vues enfants,Child
contrairement aux vues. Donc, dans mon exemple,ParentView
etPhoneListView
sont desParent
vues, tandis queInfoView
et lesPhoneView
entrées sont desChild
vues.Comme je l'ai mentionné précédemment, la plus grande différence entre ces deux catégories est le moment où elles sont autorisées à effectuer le rendu. Dans un monde parfait, je veux que les
Parent
vues ne soient rendues qu'une seule fois. Il appartient à leur vue enfant de gérer tout nouveau rendu lorsque le ou les modèles changent.Child
vues, d'autre part, je permets de restituer à tout moment dont ils ont besoin car ils n'ont pas d'autres vues en s'appuyant sur eux.Dans un peu plus de détails, pour les
Parent
vues, j'aime mesinitialize
fonctions pour faire quelques choses:InfoView
serait attribuée#info
).L'étape 1 est assez explicite.
L'étape 2, le rendu, est effectuée de sorte que tous les éléments sur lesquels les vues enfants s'appuient existent déjà avant que j'essaie de les affecter. Ce faisant, je sais que tous les enfants
events
seront correctement définis et je peux restituer leurs blocs autant de fois que je le souhaite sans me soucier de devoir déléguer quoi que ce soit. Je n'ai pas vraimentrender
d'opinion d'enfant ici, je leur permets de le faire dans le leurinitialization
.Les étapes 3 et 4 sont en fait gérées en même temps que je passe
el
lors de la création de la vue enfant. J'aime passer un élément ici car je pense que le parent devrait déterminer où à son avis l'enfant est autorisé à mettre son contenu.Pour le rendu, j'essaie de le garder assez simple pour les
Parent
vues. Je veux que larender
fonction ne fasse rien de plus que rendre la vue parent. Aucune délégation d'événement, aucun rendu de vues enfants, rien. Juste un simple rendu.Parfois, cela ne fonctionne pas toujours. Par exemple, dans mon exemple ci-dessus, l'
#name
élément devra être mis à jour chaque fois que le nom dans le modèle change. Cependant, ce bloc fait partie duParentView
modèle et n'est pas géré par uneChild
vue dédiée , donc je contourne cela. Je vais créer une sorte desubRender
fonction qui ne remplace que le contenu de l'#name
élément, et je n'aurai pas à jeter la totalité de l'#parent
élément. Cela peut sembler être un hack, mais j'ai vraiment trouvé que cela fonctionnait mieux que d'avoir à se soucier de restituer l'ensemble du DOM et de rattacher les éléments et autres. Si je voulais vraiment le nettoyer, je créerais une nouvelleChild
vue (similaire à laInfoView
) qui gérerait le#name
bloc.Maintenant, pour les
Child
vues, leinitialization
est assez similaire auxParent
vues, juste sans la création d'autresChild
vues. Alors:Child
le rendu de vue est également très simple, il suffit de rendre et de définir le contenu de monel
. Encore une fois, pas de problème avec la délégation ou quelque chose comme ça.Voici un exemple de code de ce à quoi mon
ParentView
peut ressembler:Vous pouvez voir ma mise en œuvre
subRender
ici. En ayant des modifications liées àsubRender
au lieu derender
, je n'ai pas à me soucier de dynamiter et de reconstruire le bloc entier.Voici un exemple de code pour le
InfoView
bloc:Les liens sont la partie importante ici. En me liant à mon modèle, je n'ai jamais à me soucier de
render
m'appeler manuellement . Si le modèle change, ce bloc se restitue sans affecter les autres vues.Le
PhoneListView
sera similaire auParentView
, vous aurez juste besoin d'un peu plus de logique dans vos fonctionsinitialization
etrender
pour gérer les collections. La façon dont vous gérez la collection dépend vraiment de vous, mais vous devrez au moins écouter les événements de la collection et décider de la façon dont vous souhaitez effectuer le rendu (ajouter / supprimer ou simplement restituer le bloc entier). Personnellement, j'aime ajouter de nouvelles vues et supprimer les anciennes, pas restituer la vue entière.Le
PhoneView
sera presque identique auInfoView
, n'écoutant que les changements de modèle qui lui tiennent à cœur.J'espère que cela a aidé un peu, s'il vous plaît laissez-moi savoir si quelque chose prête à confusion ou n'est pas assez détaillé.
la source
render
à l'intérieur de lainitialize
méthode est une mauvaise pratique, car cela vous empêche d'être plus performant dans les cas où vous ne voulez pas rendre immédiatement. Que pensez-vous de ceci?PhoneListView
?Je ne sais pas si cela répond directement à votre question, mais je pense que c'est pertinent:
http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
Le contexte dans lequel j'ai mis en place cet article est bien sûr différent, mais je pense que les deux solutions que je propose, ainsi que les avantages et les inconvénients de chacune, devraient vous faire avancer dans la bonne direction.
la source
Pour moi, cela ne semble pas être la pire idée au monde de faire la différence entre la configuration initiale et les configurations ultérieures de vos vues via une sorte de drapeau. Pour rendre cela propre et facile, le drapeau doit être ajouté à votre propre vue qui devrait étendre la vue Backbone (Base).
Comme Derick, je ne suis pas tout à fait sûr que cela réponde directement à votre question, mais je pense que cela mérite au moins d'être mentionné dans ce contexte.
la source
Kevin Peel donne une excellente réponse - voici ma version tl; dr:
la source
J'essaie d'éviter le couplage entre des vues comme celles-ci. Il y a deux façons que je fais habituellement:
Utilisez un routeur
Fondamentalement, vous laissez la fonction de votre routeur initialiser la vue parent et enfant. La vue ne se connaît donc pas, mais le routeur gère tout.
Passer le même el aux deux vues
Les deux ont la connaissance du même DOM, et vous pouvez les commander comme vous le souhaitez.
la source
Ce que je fais, c'est donner à chaque enfant une identité (ce que Backbone a déjà fait pour vous: cid)
Lorsque le conteneur effectue le rendu, l'utilisation de 'cid' et 'tagName' génère un espace réservé pour chaque enfant, donc dans le modèle, les enfants n'ont aucune idée de l'endroit où il sera placé par le conteneur.
que vous ne pouvez utiliser
aucun espace réservé spécifié n'est nécessaire, et Container génère uniquement l'espace réservé plutôt que la structure DOM des enfants. Cotainer et Children génèrent toujours leurs propres éléments DOM et une seule fois.
la source
Voici un mixin léger pour créer et rendre des sous-vues, qui, je pense, résout tous les problèmes de ce fil:
https://github.com/rotundasoftware/backbone.subviews
L'approche adoptée par ce plug-in est de créer et de rendre des sous-vues après le premier rendu de la vue parent. Ensuite, lors des rendus suivants de la vue parent, $ .détachez les éléments de la sous-vue, restituez le parent, puis insérez les éléments de la sous-vue aux endroits appropriés et restituez-les. De cette façon, les sous-vues sont réutilisées sur les rendus suivants, et il n'est pas nécessaire de déléguer à nouveau les événements.
Notez que le cas d'une vue de collection (où chaque modèle de la collection est représenté avec une sous-vue) est assez différent et mérite à mon avis sa propre discussion / solution. La meilleure solution générale que je connaisse dans ce cas est le CollectionView dans Marionette .
EDIT: pour le cas de la vue de la collection, vous pouvez également consulter cette implémentation plus axée sur l'interface utilisateur , si vous avez besoin d'une sélection de modèles en fonction des clics et / ou du glisser-déposer pour la réorganisation.
la source