This is tricky. By default you cannot do that, for the reasons you already mentioned.
The object you get out is the same object you set later. So not only does it not detect the change event, you also change the data inside the model without realizing it when you do x.something = 5;
.
And it's even worse than that:
var MyModel = Backbone.Model.extend({
defaults: {
x: {}
}
});
var myModel = new MyModel();
var myModel2 = new MyModel();
var x = myModel.get('x');
x.something = 5;
console.log(myModel2.get('x'));
>> { something: 5 }
The default for x will be shared across all instances because it is part of the prototype, gets copied to the instance and still references the same object of the prototype.
So basically, you cannot use defaults for anything other than booleans, numbers and strings, and you cannot get any change events on any other types either unless you clone them before mutating or pass in new unique objects.
Backbone really does not work well at all with non-flat data structures in its attributes.
Your solution is one of the possibilities, although I would probably extend the base Backbone.Model
and put the _.clone
call into the get
method.
var DeepModel = Backbone.Model.extend({
get: function (attr) {
return _.clone(Backbone.Model.prototype.get.apply(this, arguments));
}
});
var myModel = new DeepModel({
x: {
something: 5
}
});
myModel.on('change', console.log.bind(console, 'Changed'));
var x = myModel.get('x');
x.something = 10;
myModel.set('x', x);
Then you can also override the set method to trigger more exact events so that you can also see what field changed exactly.
change:x
would be a little bit useless to know.
Of course other people have already had the same problems.
I know of at least two libraries that attempt to solve this kind of problem:
Backbone Relational
and backbone-deep-model
Backbone Relational is pretty heavy, so it might not really be the right choice to just deal with non-flat attributes on a model.
I've never used backbone-deep-model. Just check it out and see if it fits your use case.
I bet there are also other libraries that solve this issue.
Or just come up with your own way of solving this problem and open source the solution.