Search code examples
javascriptbackbone.jsbackbone-events

Why Doesn't Backbone View Detect Model Changes?


I have the following Backbone.js model and collection:

// Model 
lr.Event = Backbone.Model.extend({});

// Collection
lr.Events = Backbone.Collection.extend({
  model: lr.Event,
  initialize: function(models, options) {
    this.date = options.date;
  },
  url: function() {
    return '/events' + '?date=' + this.date.toString('yyyy-MM-dd');
  }  
});

Which are used with a View and its sub-view:

// View
lr.MapView = Backbone.View.extend({
  el: $('#wrapper'),
  initialize: function() {
    this.date = this.options.date;
    _.bindAll(this, "render", "addAllEvents", "addOneEvent", "showNextDay", "collectData");
    this.collectData();
  },
  events: {
    "click #next": "showNextDay",
  },
  collectData: function() {
    var that = this;
    if (this.collection) this.collection.reset();
    this.collection = new lr.Events([], { date : this.date });
    this.collection.fetch({
      success:  function(resp) {
        that.render();
        that.addAllEvents();
      }
    });      
  },
  showNextDay: function() {
    this.date = this.date.add(1).days();
    this.collectData();
  },
  addAllEvents: function() {
    this.collection.each(this.addOneEvent);
  },
  addOneEvent: function(e) {
    var ev = new lr.EventView({ 
      model:  e,
      parentView: this
    });
  },
  ...
});

// Sub-view
lr.EventView = Backbone.View.extend({
  initialize: function() {
    var that = this;
    this.model.bind('change', function() {
      console.log('foo');
      that.remove();
    });
  }
  ...
});

I am trying to bind the sub-view to changes to its associated model. So I would expect that when I call this.collection.reset(), console.log('foo') would be called. But it is not. I have console.loged the collection before and I after I reset it, and it is definitely going from "something to nothing," so I assume I am somehow messing up the binding of the sub-view to the model.

Anyone see the error?


Solution

  • Calling reset() with no models empties the collection, but does not 'change' the existing models. The 'change' event occurs when some attribute of the model changes. Calling reset() does trigger the 'reset' event on the collection itself (also triggered when you fetch a collection); when this is fired, you should also reset your UI, creating and rendering all the sub-view items (in your case, it looks like you would call render and addAllEvents when you get the reset event).

    this.collection.on('reset', this.removeExistingEventViewsThenAddAllEvents, this);
    

    Now, when some attribute of an Event model does change, you will hit your change handler:

    this.model.bind('change', function() {
      console.log('foo');
      that.remove();
    });
    

    But, I really doubt you want to remove the Event view when the model changes. More likely, you want to re-render it, or update some part of it using JQuery.