Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is a great question. Backbone is great because of the lack of assumptions it makes, but it does mean you have to (decide how to) implement things like this yourself. After looking through my own stuff, I find that I (kind of) use a mix of scenario 1 and scenario 2. I don't think a 4th magical scenario exists because, simply enough, everything you do in scenario 1 &amp; 2 must be done.</p> <p>I think it'd be easiest to explain how I like to handle it with an example. Say I have this simple page broken into the specified views:</p> <p><img src="https://i.stack.imgur.com/k2QMj.jpg" alt="Page Breakdown"></p> <p>Say the HTML is, after being rendered, something like this:</p> <pre><code>&lt;div id="parent"&gt; &lt;div id="name"&gt;Person: Kevin Peel&lt;/div&gt; &lt;div id="info"&gt; First name: &lt;span class="first_name"&gt;Kevin&lt;/span&gt;&lt;br /&gt; Last name: &lt;span class="last_name"&gt;Peel&lt;/span&gt;&lt;br /&gt; &lt;/div&gt; &lt;div&gt;Phone Numbers:&lt;/div&gt; &lt;div id="phone_numbers"&gt; &lt;div&gt;#1: 123-456-7890&lt;/div&gt; &lt;div&gt;#2: 456-789-0123&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; </code></pre> <p>Hopefully it's pretty obvious how the HTML matches up with the diagram. </p> <p>The <code>ParentView</code> holds 2 child views, <code>InfoView</code> and <code>PhoneListView</code> as well as a few extra divs, one of which, <code>#name</code>, needs to be set at some point. <code>PhoneListView</code> holds child views of its own, an array of <code>PhoneView</code> entries.</p> <p>So on to your actual question. I handle initialization and rendering differently based on the view type. I break my views into two types, <code>Parent</code> views and <code>Child</code> views.</p> <p>The difference between them is simple, <code>Parent</code> views hold child views while <code>Child</code> views do not. So in my example, <code>ParentView</code> and <code>PhoneListView</code> are <code>Parent</code> views, while <code>InfoView</code> and the <code>PhoneView</code> entries are <code>Child</code> views.</p> <p>Like I mentioned before, the biggest difference between these two categories is when they're allowed to render. In a perfect world, I want <code>Parent</code> views to only ever render once. It is up to their child views to handle any re-rendering when the model(s) change. <code>Child</code> views, on the other hand, I allow to re-render anytime they need since they don't have any other views relying upon them.</p> <p>In a little more detail, for <code>Parent</code> views I like my <code>initialize</code> functions to do a few things:</p> <ol> <li>Initialize my own view</li> <li>Render my own view</li> <li>Create and initialize any child views.</li> <li>Assign each child view an element within my view (e.g. the <code>InfoView</code> would be assigned <code>#info</code>).</li> </ol> <p>Step 1 is pretty self explanatory.</p> <p>Step 2, the rendering, is done so that any elements the child views rely on already exist before I try to assign them. By doing this, I know all child <code>events</code> will be correctly set, and I can re-render their blocks as many times as I want without worrying about having to re-delegate anything. I do not actually <code>render</code> any child views here, I allow them to do that within their own <code>initialization</code>.</p> <p>Steps 3 and 4 are actually handled at the same time as I pass <code>el</code> in while creating the child view. I like to pass an element in here as I feel the parent should determine where in its own view the child is allowed to put its content.</p> <p>For rendering, I try to keep it pretty simple for <code>Parent</code> views. I want the <code>render</code> function to do nothing more than render the parent view. No event delegation, no rendering of child views, nothing. Just a simple render.</p> <p>Sometimes this doesn't always work though. For instance in my example above, the <code>#name</code> element will need to be updated any time the name within the model changes. However, this block is part of the <code>ParentView</code> template and not handled by a dedicated <code>Child</code> view, so I work around that. I will create some sort of <code>subRender</code> function that <em>only</em> replaces the content of the <code>#name</code> element, and not have to trash the whole <code>#parent</code> element. This may seem like a hack, but I've really found it works better than having to worry about re-rendering the whole DOM and reattaching elements and such. If I really wanted to make it clean, I'd create a new <code>Child</code> view (similar to the <code>InfoView</code>) that would handle the <code>#name</code> block.</p> <p>Now for <code>Child</code> views, the <code>initialization</code> is pretty similar to <code>Parent</code> views, just without the creation of any further <code>Child</code> views. So:</p> <ol> <li>Initialize my view</li> <li>Setup binds listening for any changes to the model I care about</li> <li>Render my view</li> </ol> <p><code>Child</code> view rendering is also very simple, just render and set the content of my <code>el</code>. Again, no messing with delegation or anything like that.</p> <p>Here is some example code of what my <code>ParentView</code> may look like:</p> <pre><code>var ParentView = Backbone.View.extend({ el: "#parent", initialize: function() { // Step 1, (init) I want to know anytime the name changes this.model.bind("change:first_name", this.subRender, this); this.model.bind("change:last_name", this.subRender, this); // Step 2, render my own view this.render(); // Step 3/4, create the children and assign elements this.infoView = new InfoView({el: "#info", model: this.model}); this.phoneListView = new PhoneListView({el: "#phone_numbers", model: this.model}); }, render: function() { // Render my template this.$el.html(this.template()); // Render the name this.subRender(); }, subRender: function() { // Set our name block and only our name block $("#name").html("Person: " + this.model.first_name + " " + this.model.last_name); } }); </code></pre> <p>You can see my implementation of <code>subRender</code> here. By having changes bound to <code>subRender</code> instead of <code>render</code>, I don't have to worry about blasting away and rebuilding the whole block.</p> <p>Here's example code for the <code>InfoView</code> block:</p> <pre><code>var InfoView = Backbone.View.extend({ initialize: function() { // I want to re-render on changes this.model.bind("change", this.render, this); // Render this.render(); }, render: function() { // Just render my template this.$el.html(this.template()); } }); </code></pre> <p>The binds are the important part here. By binding to my model, I never have to worry about manually calling <code>render</code> myself. If the model changes, this block will re-render itself without affecting any other views.</p> <p>The <code>PhoneListView</code> will be similar to the <code>ParentView</code>, you'll just need a little more logic in both your <code>initialization</code> and <code>render</code> functions to handle collections. How you handle the collection is really up to you, but you'll at least need to be listening to the collection events and deciding how you want to render (append/remove, or just re-render the whole block). I personally like to append new views and remove old ones, not re-render the whole view.</p> <p>The <code>PhoneView</code> will be almost identical to the <code>InfoView</code>, only listening to the model changes it cares about.</p> <p>Hopefully this has helped a little, please let me know if anything is confusing or not detailed enough.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload