Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone JS complex model fetch

I have two backbone models, loaded from server:

var Model = Backbone.Model.extend({});
var SubModel = Backbone.Model.extend({});

var SubCollection = Backbone.Collection.extend({
    model: SubModel
});

var m = new Model();
m.fetch({success: function(model)
{
    model.submodels = new SubCollection();
    model.submodels.url = "/sub/" + model.get("id");
    model.submodels.fetch();
}});

So, the server has to send two separate responses. For example:

{ name: "Model1", id: 1 } // For Model fetch

and

[{ name: "Submodel1", id: 1 }, { name: "Submodel2", id: 2 }] // For Submodel collection fetch

Is there a way to fetch a Model instance with Submodel collection at once, like:

{ 
  name: "Model1", 
  id: 1, 
  submodels: [{ name: "Submodel1", id: 2 }, { name: "Submodel1", id: 2 }]
}
like image 683
Just_Mad Avatar asked Mar 28 '12 20:03

Just_Mad


1 Answers

To be able to do that is up to your back-end - it doesn't really have anything to do with Backbone.

Can you configure your back-end technology to return related models as nested resources?

If your back-end is Rails, for instance, and your models are related in ActiveRecord, one way of doing this is something like

respond_to do |format|
  format.json  { render :json => @model.to_json(:include => [:submodels])}
end

What back-end technology are you using?

Edit:

Sorry, misunderstood the gist of your question, once you've got your back-end returning the JSON in the proper format, yeah, there are things you need to do in Backbone to be able to handle it.

Backbone-Relational

One way to deal with it is to use Backbone-Relational, a plugin for handling related models.

You define related models through a 'relations' property:

SubModel = Backbone.RelationalModel.extend({});

SubCollection = Backbone.Collection.extend({
    model: SubModel
});

Model = Backbone.RelationalModel.extend({
  relations: [
    {
        type: 'HasMany',
        key: 'submodels',
        relatedModel: 'SubModel',
        collectionType: 'SubCollection'
    }
  ]
});

When your Model fetches the JSON, it will automatically create a SubCollection under the 'submodels' property and populate it with SubModels - one for each JSON object in the array.

jsfiddle for backbone-relational: http://jsfiddle.net/4Zx5X/12/

By Hand

You can do this by hand if you want as well. In involves overriding the parse function for your Model class (forgive me if my JS is not 100% correct - been doing CoffeeScript so much lately its hardwired in my brain)

var Model = Backbone.Model.extend({
  parse: function(response) {
    this.submodels = new SubCollection();
    // Populate your submodels with the data from the response.
    // Could also use .add() if you wanted events for each one.
    this.submodels.reset(response.submodels);
    // now that we've handled that data, delete it
    delete response.submodels;
    // return the rest of the data to be handled by Backbone normally.
    return response;
  }
});

parse() runs before initialize() and before the attributes hash is set up, so you can't access model.attributes, and model.set() fails, so we have to set the collection as a direct property of the model, and not as a "property" that you would access with get/set.

Depending on what you want to happen on "save()" you may have to override `toJSON' to get your serialized version of the model to look like what your API expects.

jsfiddle:

http://jsfiddle.net/QEdmB/44/

like image 182
Edward M Smith Avatar answered Oct 30 '22 00:10

Edward M Smith