简体   繁体   中英

Backbone.js On Trigger Callback Binding Not Working as Expected

I have a Backbone Collection that needs to fetch whenever another Backbone Model (no part of the Collection) changes.

When I write it like this:

this.fModel = new FooModel();
this.bCollection = new BarCollection();
this.fModel.on("change", this.bCollection.fetch, this)

I get the following error when the change event is triggered:

Uncaught TypeError: Object #<Object> has no method 'trigger'

However, when I simply wrap the Collection's fetch call, it works as expected:

this.fModel = new FooModel();
this.bCollection = new BarCollection();
this.testfunc = function(){
    this.bCollection.fetch();
}
this.fModel.on("change", this.testfunc, this)

Why is this the case? Thanks!

This is a fun one to try and explain :)

So when you call on like this:

this.fModel.on('change', this.bCollection.fetch, this);

You are setting the context that fetch is run in to whatever this is. In this code it looks like this is simply your top-level application or similar. fetch can't do much with that! Let's look at the implementation of fetch :

// Fetch the default set of models for this collection, resetting the
// collection when they arrive. If `add: true` is passed, appends the
// models to the collection instead of resetting.
fetch: function(options) {
  options = options ? _.clone(options) : {};
  if (options.parse === undefined) options.parse = true;
  var collection = this;
  var success = options.success;
  options.success = function(resp, status, xhr) {
    collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
    if (success) success(collection, resp);
  };
  options.error = Backbone.wrapError(options.error, collection, options);
  return (this.sync || Backbone.sync).call(this, 'read', this, options);
},

So we basically make it up to var collection = this; ... Oops!

We've set collection inside fetch to be your top-level application!


So the reason it works when you wrap it is even more fun:

var wrapped = function() { this.bCollection.fetch(); };
this.fModel.on('change', wrapped, this);

We've set the context of wrapped to be this . That's fine because this.bCollection is exactly what we want. But when you call fetch on bCollection here, it's doing it in the normal way, binding this inside it to the object it was called on - this is normal javascript stuff now.


So, here's a TL;DR:

You actually want:

this.fModel.on('change', this.bCollection.fetch, this.bCollection);

Because the context of the fetch function call should be the collection itself, and nothing else.

Make sense?

Cheers :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM