Search code examples
javascriptjquerybackbone.js

Backbone listenTo does not fire jquery function as a handler


I have problem with Backbone's events system.

Is it possible to pass jquery function as callback directly?

Following code does not fire show/hide methods:

initialize: function () {
  this.render();
  this.logoutButton = $('#logout-button');
  this.logoutButton.hide();
  this.listenTo(this.model, 'loginSuccessEvent', this.logoutButton.show);
  this.listenTo(this.model, 'logoutSuccessEvent', this.logoutButton.hide);
},

But if I change it to this, it works perfectly:

initialize: function () {
  this.render();
  this.logoutButton = $('#logout-button');
  this.logoutButton.hide();
  this.listenTo(this.model, 'loginSuccessEvent', this.showButton);
  this.listenTo(this.model, 'logoutSuccessEvent', this.hideButton);
},

showButton: function () {
  this.logoutButton.show();
},

hideButton: function () {
  this.logoutButton.hide();
}

Solution

  • From the fine manual:

    listenTo object.listenTo(other, event, callback)
    [...]
    The callback will always be called with object as context.

    So when you say this:

    this.listenTo(this.model, 'loginSuccessEvent', this.logoutButton.show);
    

    you're really saying:

    var show = this.logoutButton.show;
    this.listenTo(this.model, 'loginSuccessEvent', show);
    

    And then Backbone will call show more or less like this:

    your_view.show(arg, ...);
    // Or internally:
    show.apply(your_view, arguments);
    

    so when show (which is jQuery's show) gets called, its this will be your view rather than logoutButton. Remember that this inside a JavaScript function depends on how the function is called rather than where it is defined (except for bound functions of course).

    You have some options:

    1. Use your showButton and hideButton functions.

    2. Use an anonymous function:

      this.listenTo(this.model, 'loginSuccessEvent', function() {
          this.logoutButton.show();
      });
      
    3. Use a bound function:

      this.listenTo(this.model, 'loginSuccessEvent', this.logoutButton.show.bind(this.logoutButton));
      

      Careful with this though, show will be called with the arguments that listenTo would normally use so you might need to supply more arguments to bind to avoid confusing show and hide with arguments they're not expecting.