Reputation: 110892
I have a JSON response with the following structure:
{
id: 1,
status: 0,
created_at: "Y:m:d H:m:s",
updated_at "Y:m:d H:m:s)",
order_items: [items]
}
Could I make a collection with it? The ID, status etc. are just metadata. Or should I create new ItemsCollection
for the items array? Also will I get notified when an item changed?
Upvotes: 5
Views: 542
Reputation: 910
Yes, you're on the right track. You simply have to define three things:
Model
for your order
objectModel
for the order_item
objectCollection
for order_item
sRemember that every Model
is responsible for its own validation, so you need to write a validate function for it.
Then, every time you parse your JSON, in your item's parse
method, you need to convert the order_item
s to a backbone Collection
using something like:
parse: function(response) {
if (!_.isNull(response) && !_.isNull(response.orderitems)) {
response.orderitems = new OrderItems(response.orderitems);
}
return response;
}
Once you've done this, everything should work as-intended!
Upvotes: 6
Reputation: 8407
As described in Backbone (http://backbonejs.org/#FAQ-nested) you should use the following way...
Edit: You do not need any plugins. Futhermore I do not recommend this, as with Backbone you can be sure you have a rock-solid framework, but they do not check how rock-solid is any plugin they propagate on their website. I had many projects where I had to think about nesting. In the end, the here shown way was the most secure and the most conventional.
var Order = Backbone.Model.extend({
initialize : function(data) {
this.orderItems = new OrderItems(null, {order:this});
if (!!data.order_items) {
this.orderItems.reset(data.order_items, {parse:true});
//we dont want nesting in "attributes"
this.unset("order_items");
}
},
// this is used, when a change from server happens. So when
// the model already exists, and the stuff in initialize
// wouldnt be called anymore.
parse : function(data) {
if (!!data.order_items) {
this.orderItems.reset(data.order_items);
delete data.order_items;
}
return data;
}
});
var Orders = Backbone.Collection.extend({
model : Order
});
var OrderItem = Backbone.Model.extend({
});
var OrderItems = Backbone.Collection.extend({
model : OrderItem,
initialize : function(data, options) {
if (options && options.order) {
this.order = options.order;
}
}
};
myOrder.orderItems.on("...");
myOrder.on("...");
access your Order from within the OrderItems.
var someOrderItem = myOrders.get(1).orderItems.first();
someOrderItem.collection.order === myOrders.get(1)
// will result in "true"
sometimes we dont want to load everything in one request. Most of the time I prefer to load the base collection, and only when a view does load one model, and the model has subcollections, I retrieve them later. For this I have a fetchOnce method.
var OrderItems = Backbone.Model.extend({
initialize : function() {
if (options && options.order) {
this.order = options.order;
}
this.fetchOnce = _.once(this.fetch, this);
//this will always return you an jquery xhr deferred.
},
url : function() {
if (this.order) {
// url() would return you something like "/order/1234"
return this.order.url() + "/items"
}
}
);
Then somewhere else, you would initialize and fetch your complete orders.
var orders = new Orders();
orders.fetch();
if you then do some edit view and want all order items to be present too, you would do following.
var view = new Backbone.View({
order : orders.get(1)
});
view.order.orderItems.fetchOnce().done(function() {
view.render();
});
as fetchOnce returns a new deferred if not existant, or an existing (because of _.once) you can always bind to it, if it was called already previously, you will be notified in the done too...
Upvotes: 3
Reputation: 2024
You could also define it dynamically:
var Order = Backbone.Model.extend({
initialize: function () {
this.getItems = _.memoize(this.getItems); // not necessary, but improves performance
},
getItems: function () {
var that = this;
var col = new Backbone.Collection(this.get('order_items'));
col.on('change', function() {
that.set({order_items: this.toJSON()});
});
return col;
}
})
Any change to the returned collection would automatically update the parent model. Note: the parent model can only listen for changes to 'order_items' not it's children.
Upvotes: 0
Reputation: 72858
you can do all of that, but you have to do it yourself or with a plugin. chances are, the code you end up writing will be similar to what's available in the Backbone.Relational plugin:
http://github.com/PaulUithol/Backbone-relational
i recommend using that plugin instead of rolling the code yourself
Upvotes: 3