1

I'm trying to implement nested Collections exactly like the example I found here: https://stackoverflow.com/a/17453870/295133

The only difference being is that I'm trying to store the data locally using the localStorage plugin.

Here, my Lists would be the Hotels in the example above:

var app = app || {};


(function (){
    'use strict';

    // List Collection - list of words
    //---------------------

    var listCollection = Backbone.Collection.extend({
        //referebce to this collection's model
        model: app.ListModel,
        localStorage: new Backbone.LocalStorage('translate-lists')


    });


    app.listCollection = new listCollection();


})();



(function (){
    'use strict';

    app.ListModel = Backbone.Model.extend({

         initialize: function() {
            // because initialize is called after parse
            _.defaults(this, {
                words: new app.wordCollection
            });
        },
        parse: function(response) {
            if (_.has(response, "words")) {
                this.words = new app.wordCollection(response.words, {
                    parse: true
                });
                delete response.words;
            }
            return response;
        }
    });



})();

What the localStorage does is stores the ListModels, but if I add anything to the words collection it soon disappears after I refresh.

Any ideas how I should be saving the entire nested collection?

Community
  • 1
  • 1
Adam
  • 8,849
  • 16
  • 67
  • 131
  • I just realized that maybe I'm caught up with trying to learn Backbone that I'm making things overly complicated. Backbone says themselves that they don't directly support nesting. Can't I have two separate collections and just match the content using identifiers? – Adam Oct 29 '14 at 06:16
  • would you not also need to setup local storage for the words collection? – Quince Oct 29 '14 at 12:26
  • I tried that but it doesnt work. The parse method never triggers true for _.has(response,'words'). Perhaps that might have something to do with it. – Adam Oct 29 '14 at 13:54
  • I can;t remember but does backbone local storage use the `model.toJSON()` method to store model? if so have you checked that the models in the words collection get stored? and again if they are not I think i know the reason and might have a fix – Quince Oct 29 '14 at 13:57
  • Ill look into that. Why would the listmodels get stored amd not the wordmodel? – Adam Oct 29 '14 at 13:59
  • if it uses the toJSON to create the JSON it makes a shallow pass and won't also go into the collection, ill post a possible fix as an answer, if it's not that let me know and ill remove it – Quince Oct 29 '14 at 14:20

1 Answers1

0

So got this working and it came down to something in parse but also if you want to ensure you just get the attributes out of your nested collection you should override the toJSON otherwise you get the full collection in what this returns.

  Backbone.Model.prototype.toJSON = function() {
    var json = _.clone(this.attributes);
    for (var attr in json) {
      if ((json[attr] instanceof Backbone.Model) || (json[attr] instanceof Backbone.Collection)) {
        json[attr] = json[attr].toJSON();
      }
    }
    return json;
  };

The main thing that was breaking is in the parse. Is assigns words directly to the model,

this.words = new app.wordCollection(response.words, {
                    parse: true
                });

but this means that it will not show up when toJSON is called as it is not in the attributes (it also means you can't access it via model.get)

so this should be changed to

initialize: function () {
        // because initialize is called after parse
        _.defaults(this.attributes, {
            words: new app.WordCollection()
        });
    },
    parse: function (response) {
        if (_.has(response, "words")) {
            this.attributes.words = new app.WordCollection(response.words, {
                parse: true
            });
            delete response.words;
        }
        return response;
    }

this way it is added to the attributes of the model on not directly on the model. If you look at this fiddle http://jsfiddle.net/leighking2/t2qcc7my/ and keep hitting run it will create a new model in the collection, save it in local storage then print the results to the console. each time you hit run you should see it grow by 1 (as it gets the previous results local storage) and contain the full information that you want.

Quince
  • 14,790
  • 6
  • 60
  • 69
  • Thanks. I think it's working, sort of. I added a localstorage to the wordCollection, but when I add a word to a listModel and I run this.words.fetch({reset:true}); all listModels get the same wordCollection from storage. – Adam Oct 29 '14 at 15:17
  • that's weird it's like they are sharing the same collection, this normally happens if in the defaults a new Collection has been instantiated but that doesn't look how you have setup your model so not sure about that. But surely you don;t have to run words.fetch if the collection is stored with the list model? – Quince Oct 29 '14 at 15:24
  • Shouldn't saving the ListCollection into localstorage save it's contents as well? Doesn't make any sense. Also, when parse runs the response is always "Object {title: "My List", id: "45de3e40-69bb-fe36-69f0-fad11dba1f69"}" Shouldnt I get the word collection in there? – Adam Oct 29 '14 at 16:09
  • did you try the above? as looking at the backbone local storage code it uses `JSON.strigify`, which in turn will call the models toJSON method. If you do a console.log of listCollection.toJSON() what do you get? – Quince Oct 29 '14 at 16:22
  • I get a [object Object] in my console. Sorry, how do I expose the contents of the object? – Adam Oct 29 '14 at 17:32
  • managed to figure out what was going wrong, have a look at my updated answer – Quince Oct 29 '14 at 18:21