Search code examples
javascriptbackbone.jsbackbone-local-storage

LocalStorage and URL in one Backbone collection


I have a collection, which fetches data from URL.

BarCollection = Backbone.Collection.extend({
  model: BarModel,
  url: // Some URL
});

But the problem is that I want to fetch data to this collection not only from URL, but also from local storage. I wish I could do something like that:

BarCollection = Backbone.Collection.extend({
  model: BarModel,
  url: // Some URL,
  localStorage: new Backbone.LocalStorage('bars')
});

But .fetch() method cannot get data both from url and local storage.

Simple workaround is to create two different collections: one for URL and one for local storage. And after fetching just merge them.

BarCollection = Backbone.Collection.extend({
  model: BarModel,
  url: // Some URL
});

LocalBarCollection = Backbone.Collection.extend({
  model: BarModel,
  localStorage: new Backbone.LocalStorage('local-contributors')
});

I wonder if there is a more beautiful way of doing that.


Solution

  • To enable any collection or model to synchronize from both the localStorage and a server, Backbone's sync function can be overridden:

    Backbone.sync = (function(sync) {
        return function(method, model, options) {
            options = options || {};
            var key = _.result(model, 'localStorage'),
                response;
    
            // if the localStorage property exist on the model/collection
            // first try to sync with the localStorage
            if (key) {
                switch (method) {
                    case 'create':
                    case 'update':
                        var data = model.toJSON(),
                            text = JSON.stringify(data);
                        localStorage.setItem(key, text);
                        break;
                    case 'delete':
                        localStorage.removeItem(key);
                        break;
                    case 'read':
                        response = JSON.parse(localStorage.getItem(key));
                        if (response) model.set(response, { parse: true });
                        break;
                }
            }
    
            // then, always sync with the server as it normally would
            return sync.apply(this, arguments);
        };
    })(Backbone.sync);
    

    This way, if a model or a collection as a localStorage property, it'll first sync with the localStorage, then it'll make the original sync.

    Example model and collection:

    var BarModel = Backbone.Model.extend({
        urlRoot: 'some/url',
        localStorage: function() {
            return 'bars-' + this.id;
        },
    });
    
    var BarCollection = Backbone.Collection.extend({
        model: BarModel,
        url: '/some/url',
        localStorage: 'bars',
    });