简体   繁体   中英

Backbone.js - Change Event on Array of Models Doesn't Trigger On Element Change

I have a parent Backbone Model that contains two objects.

(1) An array of Backbone Models

(2) A string

If I bind to the parent, setting the value of the string does trigger the change event, however calling set on an attribute of one of the models in the array of models does not trigger the change event on the parent.

How do I fix this so that any change to any of the models in the array triggers the parents change event?

EDIT -- Added Code by request

var myModel = Backbone.Model.extend(
  {
    defaults : {
      models : [],
      aString: 'foobar'
    }
  }
);
var foo = new myModel();
var arrayElement = Backbone.Model.extend({x: 7});
var arrayElement1 = new arrayElement({x: 7});
foo.set('models', [arrayElement1]);
foo.bind('change', function() { console.log('changed!')});
arrayElement1.set('x', 10);  //Does not trigger console log
foo.set('aString', 'barfoo'); //Does trigger console log

Backbone models don't bind anything to their attributes so foo has no way of knowing that you are changing one of its attributes behind its back. So, when you do this:

foo.set('models', [some_other_model]);
some_other_model.set(...);

you haven't actually changed foo at all, all you've done is changed one of foo 's attributes directly. A model's attributes can be anything, the model simply treats them as opaque blobs. You'll have similar problems with something like this:

o = { a: 'b' };
m.set('p', o);
o.a = 'c';

In both cases, you're directly changing a model's attribute through a reference rather than through the model's interface.

Collections, on the other hand, do listen for events on their models. Collections are collections of models so they expect their members to be models and behave accordingly.

If you want a contained model to propagate 'change' events then you'll have to do it yourself by, perhaps, overriding set to manually bind change handlers to propagate the events. You could also use an internal collection instead of an array to make propagating the events easier.


You also have a hidden bug in your defaults . The defaults are copied to new model instances but the copy is a shallow copy so your models will end up sharing the same reference to the array unless an explicit set is done to replace the reference. For example, this:

var M = Backbone.Model.extend({
    defaults: {
      a: []
    }
});
var m1 = new M();
m1.get('a').push('pancakes');
console.log(M.prototype.defaults.a);
var m2 = new M();
console.log(m2.get('a'));

will put two ['pancakes'] in the console because m1.get('a') will return M.prototype.defaults.a rather than a new empty array that is specific to m1 : http://jsfiddle.net/ambiguous/AraCu/

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