8
votes

I am working with the Todos example application bundled with the latest version of Backbone (0.9.2) while learning about Backbone.js. My question is, why is the app designed to fire the render event twice when adding a model to the Todos collection?

If I place this line within the TodoView's render function:

// Re-render the titles of the todo item.
render: function() {
  console.log("Rendering!");
  this.$el.html(this.template(this.model.toJSON()));

Then "Rendering!" appears twice in the console. I understand this is because the view binds the model's change event to the view's render:

initialize: function() {
  this.model.bind('change', this.render, this);

And render is called in addOne, which is bound to the Todos' add event:

addOne: function(todo) {
  var view = new TodoView({model: todo});
  this.$("#todo-list").append(view.render().el);
},

But is this double rendering design standard practice? It seems like the view should be rendered on creation (or entrance into the DOM), and then again if the underlying model changes. In this case, nothing is being changed, but render is being called twice.

Again, I am just learning Backbone, so I may have a basic misunderstanding that is leading to my confusion.

2

2 Answers

5
votes

Hm, had a quick look. You are right that this happens, and no it's not standard practice. The reason is a bit obscure, so bear with me ;)

The todo app is using backbone-localstorage. When you try adding a new item in the app it calls:

createOnEnter: function(e) {
  if (e.keyCode != 13) return;
  if (!this.input.val()) return;

  Todos.create({title: this.input.val()});
  this.input.val('');
},

Notice the Todos.create. Normally a create will add a model to the collection as well as save it on the server. The add event will thus be triggered. It happens though that backbone-localstorage does the following on create:

create: function(model) {
  if (!model.id) model.set(model.idAttribute, guid());
  this.data[model.id] = model;
  this.save();
  return model;
},

Notice the model.set to give the model an id. This is what triggers the (second) change event.

You can stop this from happening by changing create to do:

if (!model.id) model.id = guid();

1
votes

The re-render shouldn't happen.

Try to debug a little more. Try to bind the change event to a wrapper method like:

initialize: function(){
  this.model.bind( "change", this.renderWrapper, this );
}, 

renderWrapper: function(){
  console.log( "in the renderWrapper" );
  this.render();
}

To be sure the second render() was made for the change event bind and not for other reason.