Search code examples
backbone.js

updating a Backbone model with localStorage adapter


I'm using the localStorage adapter with Backbone and have a Settings model that I store some basic settings on. In the init function, I create the settings model like this

window.onload = function() {

    window.settings = new Settings();

If the user decides to change the settings, I do this in an updateSettings function

settings.save({language: lang});

but rather than replace the settings in localStorage, it (as describes in the second answer on this question Saving a model in local storage) just creates a new storage item each time, so every time the settings change, a new instance gets stored. That means when I load the settings in my view after init, I have to set the language settings to the last item in the storage array

s[s.length - 1].language

with the whole function looking like this

$.when(products.fetch(), settings.fetch())

            .done(function(p, s){
            var l = s[s.length - 1] ? s[s.length - 1].language : 'en-us';                       
                settings.set({'language': l});   
                _this.render();

            });
    }

but this is very impractical because I don't want to store the whole history of the settings change in localstorage. As the first SO answer linked to said, I should use an id on the model. But if I assign an id in the application init like this

window.onload = function() {

    window.settings = new Settings({id: 1});

my application never renders, i.e. it never reaches the call to the render function in this code (I'm assuming it's because there's no records). Question: how can I call fetch here

  $.when(products.fetch(), settings.fetch())

                .done(function(p, s){
                var l = s[s.length - 1] ? s[s.length - 1].language : 'en-us';                       
                    settings.set({'language': l});   
                    _this.render();

                });
        }

so what I did was create the model in the application init without assigning the id

window.onload = function() {

    window.settings = new Settings();

Then when I call fetch on this model in the initializer in the view I assign the id like this, so that the fetch always returns

   $.when(products.fetch(), settings.fetch())

                .done(function(p, s){
                var l = s[s.length - 1] ? s[s.length - 1].language : 'en-us';                       
                    settings.set({'language': l, });
                    settings.set({id :1 });
                    settings.save();   
                    _this.render();

                });
        }

then when I change the settings in the update settings function like this

settings.save({language: lang});

However, if I change the settings and refresh the page a few times, it's clear that the whole history of settings is getting saved to localStorage

settings-1: "{"0":{"language":"de",","id":1},"language":"ja","id":1}"

(note, I'm actually saving more than just language settings)

Question: how can I set the id on a Backbone model so that localStorage updates the model in storage (rather than keeping a history of updates)?


Solution

  • Using the localStorage adapter with a singleton resource doesn't make much sense. I suggest to implement the sync method in your Settings Backbone model. Check the Backbone documentation, but in general the function signature is: function(method, model, options) Where the method can be: read, create, update, delete Just use the localStorage.getItem and localStorage.setItem, and something like this should work:

    
    sync: function(method, model, options) {
      var key = 'settings';
      switch(method) {
        case 'read':
          try {
            data = JSON.parse(localStorage.readItem(key));
            model.set(data);
          } catch(e) {}      
          break;
        case 'update'
        case 'create'
          localStorage.setItem(key, JSON.stringify(model.toJSON()));
          break;
        case 'delete'
          localStorage.setItem(key, null)
      }
    }
    
    

    I didn't test it, but it should be something like this.