Search code examples
eventsviewbackbone.jstriggersrender

Backbone.js - event trigger not work after rendering other views


There's a addPost function in my router. I don't want to re-create the postAddView every time the function is invoked:

addPost: function () {
  var that = this;
  if (!this.postAddView) {
    this.postAddView = new PostAddView({
      model: new Post()
    });
    this.postAddView.on('back', function () {
      that.navigate('#/post/list', { trigger: true });
    });
  }

  this.elms['page-content'].html(this.postAddView.render().el);
}

Here's the PostAddView:

PostAddView = backbone.View.extend({
  events: {
    'click #post-add-back': 'back'
  }
  , back: function (e) {
    e.preventDefault();
    this.trigger('back');
  }
});

The first time the postAddView is rendered, the event trigger works well. However, after rendering other views to page-content and render postAddView back, the event trigger won't be trigger anymore. The following version of addPost works well, though.

addPost: function () {
  var that = this, view;

  view = new PostAddView({
    model: new Post()
  });

  this.elms['page-content'].html(view.render().el);

  view.on('back', function () {
    delete view;
    that.navigate('#/post/list', { trigger: true });
  });
}

Solution

  • Somewhere you are calling jQuery's remove and that

    In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed.

    so the delegate call that Backbone uses to bind events to your postAddView.el will be lost. Then, when you re-add your postAddView.el, there are is no delegate attached anymore and no events are triggered. Note that Backbone.View's standard remove method calls jQuery's remove; a few other things in jQuery, just as empty will do similar things to event handlers. So the actual function call that is killing your delegate could be hidden deep inside something else.

    You could try calling delegateEvents manually:

    this.elms['page-content'].html(this.postAddView.render().el);
    this.postAddView.delegateEvents();
    

    or better, just throw the view away and create a new one every time you need it. Your view objects should be pretty light weight so creating new ones should be cheap and a lot less hassle than trying to keep track of the existing views by hand.