Vasiliy Nikolaev
Vasiliy Nikolaev

Reputation: 97

Structure of Backbone app. Model/Collection issues

Logic of my app is quite straightforward. I have Model that fetching JSONP (data contains years, quarter, and quarter contains different key:value). After fetching data, View listens for Model's "change" event and then rendering data. So far so good.

But then I need to listen for a "change:year"/"change:quarter" and fetch data with new url, based on changes. But when "change:year"/"change:quarter" fired, global "change" is fired also.

Sure, if I'll use Collection instead of Model – theres is "reset" for me. But in this case I can't listen for "change:year" so easily – I have to create few more Models (for year, quarter and each key in quarters). But I want to keep app simple.

Is there other way around? Or creating much more Model is inevitable?

Thanks.

UPD: "change" event still needed, because Model have to rerender, after fetching new data.

SOLUTION: I've created common Controller, that holds atributes, that needed to be changed. And in .url() method getting them from Controller to modify url. I stick to Collection's fetch()/"reset" notifying and made listener for Controller on "change". So, when I'm changing "year"/"quarter", View get notification from Controller, and when corresponding Collection fetching - it's fetching with new url.

Upvotes: 3

Views: 250

Answers (2)

Mesut Tasci
Mesut Tasci

Reputation: 3120

You can check changed attributes in model's change listener.

var MyModel=Backbone.Model.extend({
    initialize:function(){
      this.bind("change",this.changeModel);
      this.bind("change:year",this.changeYear);

   },
   changeModel:function(model,objs){
      //Checking changed attributes
      if (_.has(objs.changes,"year") || _.has(objs.changes,"quarter")) return this;

     console.log("Processing",objs.changes);
     return this;
  },
   changeYear:function(model,new_val){
    console.log("year changed.",new_val);
    return this;
  }

});


var mymodel=new MyModel();
mymodel.set("year",2012); 
mymodel.set("month",6);

If you have a view that listening model's change event, you should add a different attribute to the model and listen this attribute's change event instead of model's change event and render view when this attribute change. Change this attribute's value in changeModel function and fire listeners if your "year" and "quarter" attributes not in changed attributes as below.

//Edit changeModel as below
changeModel:function(model,objs){
          //Checking changed attributes
          if (_.has(objs.changes,"year") || _.has(objs.changes,"quarter")) return this;

          this.set("rerender",!this.rerender); //Changing for triggering change event
         return this;
      },


//View's constructor function
initialize:function(){
   this.model.bind("change:rerender",this.render);
}

Upvotes: 1

user920041
user920041

Reputation:

As I understand you, you don't want to receive the global model change event if only year or quarter changed - but you have to.

So instead when getting the receiving the global change event, you can check if it was onlyyear or quarter that was changed, and the skip the normal event handling. Something like this:

this.model.bind("change", this.render, this);
this.model.bind("change:year", this.fetchSomething, this);
this.model.bind("change:quarter", this.fetchSomething, this); 

render : function(){
    var attributesExceptYearAndQuarter = _.without(this.model.attributes, ['year', 'quarter']) 
    if(this.model.changedAttributes(attributesExceptYearAndQuarter).length > 0){
         // do the rendering
    } 
}

Upvotes: 1

Related Questions