Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a composite pattern using MVC/Backbone.js

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?

like image 365
wheresrhys Avatar asked Jan 16 '12 22:01

wheresrhys


3 Answers

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

like image 33
MauroPorras Avatar answered Sep 29 '22 10:09

MauroPorras


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!

like image 32
Tom Tu Avatar answered Sep 29 '22 10:09

Tom Tu


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!");
    }
});
like image 149
Kevin Peel Avatar answered Sep 29 '22 11:09

Kevin Peel