13

I'm learning Backbone.

I want to create a list that can contain different models, with different attributes.

For example, listing folder contents, which could include models of type file and models of type folder, in any order.

file : {
  title : "",
  date : "",
  type : "",
  yaddayadda : ""
}

folder : {
  title : "",
  date : "",
  haminahamina : ""
}

What is the proper way to represent this in Backbone? Is it possible to have a single Collection with multiple models?

user1031947
  • 6,294
  • 16
  • 55
  • 88
  • Duplicate: http://stackoverflow.com/questions/6933524/a-backbone-js-collection-of-multiple-model-subclasses/6934682#6934682 – treecoder Apr 26 '13 at 12:33

4 Answers4

26

Create a base model that your other models inherit from:

var DataModel = Backbone.Model.extend({
    // Whatever you want in here
});

var FileModel = DataModel.extend({
    // Whatever you want in here
});

var FolderModel = DataModel.extend({
    // Whatever you want in here
});

And make the model type of the collection be that same base model:

var DataCollection = Backbone.Collection.extend({
    model: DataModel
});
Lukas
  • 9,765
  • 2
  • 37
  • 45
  • Thanks for the response. When DataCollection is passed to a view to be rendered, how would you render an attribute that belongs to FileModel or FolderModel? – user1031947 Jan 10 '13 at 17:51
  • 1
    You can always check if `obj instanceof FileModel` (or `FolderModel`) and render appropriately. – Lukas Jan 10 '13 at 17:54
  • 1
    That's how I would do this as well but how are you passing in the DataCollection that you're speaking of (AJAX call)? If that's the case, you will have to check if that unique property exists on the response (ie, if (someModel.someUniqueProperty) { // render it with a view that will handle that property value};). – Dennis Rongo Jan 11 '13 at 06:27
17

You could also do it the backbone way. Check out the docs backbone collection

Basically you would create different models adding a tie breaker attribute say "type" in this case.

var file = Backbone.Model.extend({
        defaults: {
            // will need to include a tie breaker attribute in both models
            type: 'file'
        }
    }),
    folder = Backbone.Model.extend({
        defaults: {
            // tie breaker
            type: 'folder'
        }
    });

var fs = Backbone.Collection.extend({
    model: function(model, options) {
        switch(model.type) {
            case 'file':
                return new file(model, options);
            case 'folder':
                return new folder(model, options);
        }
    }
});

// after that just add models to the collection as always
new fs([
    {type: 'file',name: 'file.txt'},
    {type: 'folder',name: 'Documents'}
]);
Osvaldo Maria
  • 340
  • 5
  • 14
  • 3
    i like this method, not only is it "the backbone way" but it doesn't require the extra business logic @dennis mentioned above, in fact you'll never need any workarounds at all because it will all work like you would expect it to. also, i believe you meant the line to read: `var fs = Backbone.Collection.extend({` – jessie james jackson taylor Nov 08 '14 at 10:44
  • Please note that this breaks `this.model.prototype.idAttribute`, so you should take a look at Maciej's [answer](http://stackoverflow.com/a/35603643/1218980) which solves this with `ModelFactory.prototype.idAttribute`. – Emile Bergeron Jul 07 '16 at 14:22
  • Or just override the collection `modelId` function to avoid duplicates. – Emile Bergeron Jul 19 '16 at 18:03
1

Backbone documention is not complete in this case. It will not work when used with merge:true option and idAttribute. In that case you need to:

var ModelFactory = function (attr, options) {
  switch (attr.type) {
    case 'file':
      return new file(attr, options);
    case 'folder':
      return new folder(attr, options);
  }
};
ModelFactory.prototype.idAttribute = '_id';

var fs = Backbone.Model.extend({
   model: ModelFactory
});
Maciej Dzikowicki
  • 859
  • 1
  • 10
  • 10
-2
        var bannedList = app.request('rest:getBan');
        var whiteIpList = app.request("rest:getWhite");
        var whiteGroupList = app.request("rest:....");
        $.when(bannedList, whiteIpList, whiteGroupList).
done(function (bannedList, whiteIpList, whiteGroupList) {
            var collection = new Backbone.Collection();
            collection.add(bannedList);
            collection.add(whiteIpList);
            collection.add(whiteGroupList);

        });


    app.reqres.setHandler("rest:getBannedList", function (data) {
        return API.getBannedList(data);
    });
    getBannedList: function (data) {
                var user = new Backbone.Model();
                user.url = '/banned';
                user.cid = 'bannedList';
                var defer = $.Deferred();

                user.fetch({
                    type: 'GET',
                    data: data,
                    success: function (data) {
                        defer.resolve(data);
                    },
                    error: function (data) {
                        defer.reject(data);
                    }
                });
                return defer.promise();
            },
zloctb
  • 10,592
  • 8
  • 70
  • 89