My webapp has a composite structure i.e. each Category collection can contain a mixture of individual Items and other Categories as its rows/nodes/children (not sure of the correct terminology here). In actual fact, it's a little bit simpler than that as each collection is represented by a model, Category, so essentially each Category collection has both Item models and Category models as its children.
In general is this an advisable way to implement this structure using MVC? More specifically, in Backbone.js is it possible for a collection to have a model factory (taking the json and calculating which model to generate based on the json's structure) instead of a static model property?
Yes, you can. I have done it before. I think this links could help you: http://documentcloud.github.com/backbone/#Collection-model
Here is one of the main scripts I used for my project: https://gist.github.com/b65893e0c2e3c46d3dc1
It's fairly simply achieved by overwriting the built in public methods parse and toJSON used internally by backbone when retrieving and saving model's data.
Firstly when you retrive the Model from the database you should overwrite the model's parse
method to create models representing given items from your example.
Then on save the toJSON
method is used to serialize the data back to what the server can understand - there you'd just call the toJSON
method on each item's Model to serialize it to a format that backend will recognize. If you look at the code for Backbone.sync
you will see that the model always gets serialized using the toJSON if no custom data is passed.
Let me know if you needed more detailed info though I believe you should be able to pick it up from here!
I'm assuming you're receiving a Category/Items list in JSON that looks something like this...
{
'id': 1,
'name': 'My 1st Category',
'children': [
{
'id': 2,
'name': 'My 2nd Category',
'children': []
},
{
'id': 1,
'name': 'An Item',
'price': 109.99
}
]
}
Backbone.js doesn't have anything out of the box that supports multiple models in a collection, but it also doesn't have any restrictions on the types of models you put in a collection.
Specifying the model type in the collection definition only does one thing, it lets Backbone know what model type to create if you pass raw JSON to the collection instead of a Backbone.Model
object. If you add an Item
model to a collection that already contains a few Category
Models, Backbone will have no problem popping it into the models list; it doesn't do any type checking.
So with that in mind, you can use just about everything the collection offers except pass it raw JSON; you'll need to handle that yourself. So your choices are to either build up your models beforehand, making them into Backbone.Model
objects, or create something that'll do the parsing for you.
For the second option, the parser, I'd suggest passing a special variable to the collection that contains your raw JSON, then handling that in your initialize
function. Here's an example:
var CategoryCollection = Backbone.Collection.extend({
initialize: function(m, models) {
_.each(models, function(model) {
var modelObject = null;
if (model.price !== undefined) {
modelObject = new Item(model);
} else {
modelObject = new Category(model);
}
this.add(modelObject);
}, this);
}
});
So it is a little hacky, but you determine the type of model based on if it has a specific field (price
in my example), create the model object, then add that to the collection.
You'd then call it this way:
var myCollection = new CategoryCollection([], myJSON);
Notice you have to pass an empty array as the first argument since that's how'd you normally pass a set of models to the collection.
Later on when using the collection, you can determine if you're dealing with an Item
or Category
using a simple instanceof
check:
_.each(myCollection.models, function(model) {
if (model instanceof Item) {
console.log("It's an Item! Price: ", model.get("price"));
} else {
console.log("It's a Category!");
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With