18

Is it technically possible to nest views, using templating, something like that:

<%= new PhotoCollectionView({model:new PhotoCollection(model.similarPhotos)}).render().el) %>

I can put all the stuff in the render method as well, but templating gives much more room for flexibility and layout.

I tried the aforementioned variant, but all I get as a result on the screen is [HTMLDivElement].

If I try to extract just the HTML out ouf it, using jQuery's HTML, I get it rendered, but it turns out that the DOM nodes that get printed out are different from the ones that the views hold a reference to, because no interaction whatsoever with those DOM nodes is possible using the view instance. For instance if within the view I say $(this.el).hide(), nothing will happen.

What is the proper way, if any?

Mrchief
  • 75,126
  • 20
  • 142
  • 189
user802232
  • 2,541
  • 7
  • 34
  • 40
  • 1
    I'm not sure whether this technique is technically possible or not, but I would recommend against it, as templates should only contain simple conditional statements and not any application logic. The application logic should remain in your Views. This way, there is clear separation of concern and your application will be more maintainable. – Johnny Oshika Aug 21 '11 at 15:47
  • you can nest views using React.js – Alexander Mills Aug 25 '15 at 20:35

5 Answers5

29

I typically render the parent view first. I then use the this.$('selector') method to find a child element that I can use as the el of the child view.

Here is a full example:

var ChildView = Backbone.View.extend({
  //..
})

var ParentView = Backbone.View.extend({
  template: _.template($('#parent-template').html()),
  initialize: function() {
    _.bindAll(this, 'render');
  }
  render: function() {
    var child_view = new ChildView({ el: this.$('#child-el') }); //This refers to ParentView. 
    return this;
  }
});

var v = new ParentView();
v.render(); 
Johnny Oshika
  • 54,741
  • 40
  • 181
  • 275
Skylar Anderson
  • 5,643
  • 1
  • 25
  • 34
  • well, yeah ... I ended up doing the same – user802232 Aug 23 '11 at 21:26
  • 6
    This seems a little inefficient. You are re-initializing the child view each time the parent is rendered. – Alessandro Giannone Jan 04 '13 at 14:28
  • 5
    Agree with Alessandro. In addition to being inefficient you also loose dynamic state in child views every time parent view is rendered. Check out [Backbone.Subviews](https://github.com/rotundasoftware/backbone.subviews) for ready to use minimalist solution – Brave Dave Feb 01 '13 at 20:00
  • 2
    You should create the child view in initialize, and store it as a property of the parent view. Then call render on the child view inside the parent's render function. – michael.orchard Feb 20 '13 at 09:10
14

The accepted answer has a major flaw, which is the fact that the ChildView is going to be re-initialised everytime it's rendered. This means you will lose state and potentially have to re-initialised complicated views on each render.

I wrote a blog about this here: http://codehustler.org/blog/rendering-nested-views-backbone-js/

To summarise though, I would suggest using something like this:

var BaseView = Backbone.View.extend({

    // Other code here...

    renderNested: function( view, selector ) {
        var $element = ( selector instanceof $ ) ? selector : this.$el.find( selector );
        view.setElement( $element ).render();
    }
});

var CustomView = BaseView.extend({

    // Other code here...

    render: function() {
        this.$el.html( this.template() );
        this.renderNested( this.nestedView, ".selector" );
        return this;
    }
});

You do not need to extend the Backbone view if you don't want to, the renderNested method could be put anywhere you like.

With the code above, you can now initialise the ChildView in the initialisation method and then simply render it when render() is called.

Alessandro Giannone
  • 885
  • 1
  • 10
  • 27
2

Check out the Backbone.Subviews mixin. It is a minimalist mixin built for managing nested views and does not re-initialize the child views every time the parent is rendered.

Brave Dave
  • 1,300
  • 14
  • 9
1

I don't know about within a template itself, but I've done it with tables and lists before. In the outer template, just have the stub:

<script type="text/template" id="table-template">
    <table>
        <thead>
            <th>Column 1</th>
        </thead>
        <tbody>
        </tbody>
    </table>
</script>

and for the individual items: <%= field1 %>

then in your render method, just render the individual items and append them to the tbody element...

Bryce Fischer
  • 5,336
  • 9
  • 30
  • 36
-1

The decision to initialize a new object every time you render seems to me very inefficient. Particularly this:

render: function() {
    var child_view = new ChildView({ el: this.$('#child-el') }); //This refers to ParentView. 
    return this;
  }

Ideally the Rendering of the parent should be something like

render: function() {
   this.$el.html(this.template());
   this.childView1.render();
   this.childView2.render();
}

And the children creation should happen only when initializing the parent:

initialize: function() {
       this.childView1 = new ChildView1(selector1);
       this.childView2 = new ChildView2(selector2);
} 

the problem is that we do not have selector1 and selector2 before rendering the parent template. This is where I am stuck at right now :)

Andrey Petrov
  • 2,291
  • 2
  • 15
  • 12