Cimm
Cimm

Reputation: 4775

Render a Backbone.js collection

I am a Backbone.js n00b and trying to get my head around it. I know how to render a model using a view and the built-in underscore.js templating engine. Now I'm trying to render a collection and that's where I get stuck. There is no server here, so I'm not fetching anything remotely, just a simple HTML page with some JavaScript.

ContinentModel = Backbone.Model.extend({});

ContinentsCollection = Backbone.Collection.extend({
  model: ContinentModel,

  initialize: function () {
    this.continentsView = new ContinentsView;
    this.bind("reset", this.continentsView.render);
  }
});

ContinentsView = Backbone.View.extend({
  el: '#continents',
  template: _.template($('#continents-template').html()),

  render: function() {
    var renderedContent = this.template(this.collection.toJSON());
    $(this.el).html(renderedContent);
    return this;
  }
});

$(function() {
  var continentsCollection = new ContinentsCollection();
  continentsCollection.reset([{name: "Asia"}, {name: "Africa"}]);
});

It breaks on the template attribute line in the view but I'm not sure that's where I need to look. Am I supposed to render a collection or do I miss the point completely here (maybe collections are just grouping objects and I shouldn't look at it as a list I can render)?

Thanks for helping...

Upvotes: 17

Views: 24067

Answers (2)

dira
dira

Reputation: 4791

The problem is that when you define ContinentsView, the template is evaluated and it uses $('#continents-template') - but the DOM is not ready yet, so it does not find the template.

To solve it, simply move the template assignment in the initialize function:

ContinentsView = Backbone.View.extend({
  el: '#continents',
  initialize: function() {
     this.template = _.template($('#continents-template').html());
  }
  ...

Regarding collections, yes, they are grouping objects, specifically sets of models.

You should make the code so the models (and collections) do NOT know about the views, only the views know about models.

ContinentModel = Backbone.Model.extend({});

ContinentsCollection = Backbone.Collection.extend({
  model: ContinentModel,
  // no reference to any view here    
});

ContinentsView = Backbone.View.extend({
  el: '#continents',

  initialize: function() {
    this.template = _.template($('#continents-template').html());
    // in the view, listen for events on the model / collection
    this.collection.bind("reset", this.render, this);
  },

  render: function() {
    var renderedContent = this.template(this.collection.toJSON());
    $(this.el).html(renderedContent);
    return this;
  }
});

$(function() {
  var continentsCollection = new ContinentsCollection();
  continentsCollection.reset([{name: "Asia"}, {name: "Africa"}]);
  // initialize the view and pass the collection
  var continentsView = new ContinentsView({collection: continentsCollection});
});

Upvotes: 34

Brave Dave
Brave Dave

Reputation: 1300

It is also worth noting there are additional complexities that quickly rear their heads when rendering a collection in a view. For instance, the view generally needs to be re-rendered when models are added or removed from the collection. It isn't rocket science to implement your own solution, but it is probably worth looking into existing solutions since there are quite a few tried and tested ones out there.

Backbone.CollectionView is a robust collection view class that handles selecting models in response to mouse clicks, reordering the collection based on drag and drop, filtering visible models, etc.

Several popular frameworks built on top of backbone also provide simple collection view classes, like Backbone.Marionette, Chaplin, and Layout Manager.

Even though Backbone itself does not provide any structure for rendering a collection, it is a non-trivial problem and lots of people have different opinions on how it should be done. Luckily it is such a common need that there are quite a few good options already in the eco system.

Upvotes: 8

Related Questions