8
votes

Is it possible to have a template nested inside a template and access via backbone view?

For example I have View1 using Template1, and View2 using Template2. Template2 actually needs to be in a DIV inside of Template1. I put the DIV container for template2 inside template1 with the appropriate id, but it doesn't show when page is rendered. If I remove Template2 div container from inside Template1 and just put it in the page body it works fine.

So just wondering if this is possible, or if I have to nest the views/models, etc. to make this work?

The data in Template2 is not technically related to Template1 is just needs to display in a position on the page that's embedded in Template1.

5
Can you share some code? - u.k
I know this doesn't answer your question, but Ember js supports something close to that. You can define nested resources... which sorta allows you to nest templates within templates. emberjs.com/guides/routing/defining-your-routes - Jaspreet Singh Lidder

5 Answers

21
votes

The way I've dealt with this in the past is to define both views separately, and then when you render View1, create a new View2, render it, and insert it into View1. So:

window.View1 = Backbone.View.extend({
    render: function() {
        this.view2 = new View2();
        this.$('insert-view-here').append(this.view2.render().el);
    }
});
4
votes

You should create subviews for this.

I like to privatize subviews in closure and return the public view.

var View = (function (BV) {
    var View, Subview;

    // Only this main view knows of this subview
    Subview = BV.extend({ 
        template: _.template( subtmpl ),

        render: function () {
            this.$el.html( this.template( this.model.toJSON() ) );
            return this;
        }   
    }); 

    View = BV.extend({
        template: _.template( tmpl ),

        render: function () {
            this.$el.html( this.template( this.model.toJSON() ) );

            var subview = new SubView({ model: this.model }); 

            // replace a div in your template meant for teh subview with the real subview
            this.$el.find( "#subview" ).replaceWith( subview.render().el );

            return this;
        }   
    }); 

    return View;

}(Backbone.View));

var view = new View({ model: user });
var subview = new Subview; // Reference Error
2
votes

Another option that's useful when you need to include Template2 multiple times within Template1, say as <li> elements within a <ul>, is to pass a Template2 function into Template1. (From Rico Sta Cruz' Backbone Patterns.)

TasksList = Backbone.View.extend({
  // Template1, inlined in js.
  template: _.template([
    "<ul class='task_list'>",
      "<% items.each(function(item) { %>",
        "<%= itemTemplate(item) %>",
      "<% }); %>",
    "</ul>"
  ].join('')),

  // Template2, inlined in js.
  itemTemplate: _.template(
    "<li><%= name %></li>"
  ),

  render: function() {
    var html = this.template({
      items: tasks /* a collection */,
      itemTemplate: this.itemTemplate
    });

    $(this.el).append(html);
  }
});
0
votes

The typical way as I understand it is to think of views as complete objects that you can embed in each other. Let's say you have two views, ViewA and ViewB. The following code shows how you could append ViewB into ViewA.

# this is coffeescript, but it's easily translated to JS
class ViewA extends Backbone.View
    initialize: () ->
        this.render()

    render: ()->
        this.$el.append(new ViewB().$el)
        this.$el.append(new ViewB().$el)
        return this

You could get fancy with how ViewB is managed (assigning it to properties or whatever) or pass different constructor arguments into each ViewB instance.

0
votes

A more lean solution without the need for jQuery append() or the additional SubView object involving a double jQuery scope, is to pre-render/precompile strictly using the underscore method, and insert the subview as a string, using an inner comment tags in your main template.

View = Backbone.View.extend({
  template: _.template(...),
  prerender: function(tpl,model){// pre-render a component
      var template = _.template(tpl);
      return template(model);
  },
  render: function(){
    var model = { ... };
    var component = this.prerender(this.itemTemplate, model);
    this.$el.html(this.template(model).replace('<!--#component-->', component));
  }
});

It's useful especially if your template isn't a constant, and depends on a property of the current scope.

Noting that for the scope of this question, you'd have to pass the View2 model to the component.