Bowei Liu
Bowei Liu

Reputation: 191

Is 'change' event in Backbone.js only "reference based" change instead of "value based" change

I write one piece of test code:

var ModelHasObjectAttribute = Backbone.Model.extend({
    defaults:{
        property_value:99   ,
        object_attr:{"year":"1990"}
    },
    setYear:function(year) {  // function like this cannot trigger change
        var temp_object_attr = jQuery.extend({},  this.get('object_attr')  ); // This shallow copy is to change the reference of object_attr,
        temp_object_attr["year"] = year;            // otherwise I cannot trigger 'change' event, if the value of one attribute changed but reference not changed                   
        this.set( 'object_attr', temp_object_attr );    
    },
    changeYear:function( year ) {
        console.log("before changing, " + this.get('object_attr')['year']);
        this.get('object_attr')["year"] = year;
        console.log( this.get('object_attr'));  
    },
    initialize: function () {
        var classRef = this;
        this.on('change:property_value', function() {
            alert("heard property_value changing");
        });
        this.on('change:maxvalue', function() {
            alert("heard maxvalue changing");   
        }); 
        this.on('change:object_attr', function() {
            alert('heard object_attr changing' + classRef.get('object_attr')['year']);  
        });
    }   
});
var on_instance = new ModelHasObjectAttribute ();
on_instance.set('property_value',10);
on_instance.changeYear(2015); // this line of code does not trigger default 'change' event 
on_instance.setYear(2016);    // this one triggered 
console.log( on_instance.get('object_attr') );

For example, if I want change to be triggered when object_attr is changed, I have to change the reference of the object stored under object_attr. just modify the value of object of object_attr, will not trigger change.

How can I avoid the expensive shallow copy, and also trigger change events?

Upvotes: 1

Views: 66

Answers (1)

joews
joews

Reputation: 30330

That approach doesn't work because Backbone has no way to detect that you have changed the object_attr object. This isn't so much a Backbone issue as a general JavaScript point - you can't observe object property changes (yet).

The best way to set deep attributes with change events is to use a plugin like Backbone.DeepModel, which overrides set to work with nested attributes:

var ModelHasObjectAttribute = Backbone.Model.extend({
  // ...  
  setYear:function(year) { 
    // triggers 'change' events
    this.set('object_attr.year', year); 
  },
  // ...
}

N.B. The link is to a recent fork of DeepModel. Depending on your app setup, you may need to use the (unmaintained) original version.

Upvotes: 1

Related Questions