jeromegn/Backbone.localStorage

model.save() destroys nested collection

Closed this issue · 7 comments

Hi!
I have a problem with nested collections using your localStorage adapter.
I want so store GPS-Data whereas one GPS-track is represented with one title and many positions.

Positions are defined as a Backbone.Collection:

TrackModel = Backbone.Model.extend({
        defaults: {
            title: "",
            positions: new PositionsCollection()
        }
    })
PositionsCollection = Backbone.Collection.extend({
        model: PositionModel
    });
PositionModel = Backbone.Model.extend({
        defaults: {
            latitude: "",
            longitude: "",
            timestamp: ""
        }
    });

My data-structure should look like that:

{
    "title": "TrackTitle,
    "positions": [{
        "latitude": "...",
        "longitude": "...",
        "timestamp":"..." }],
        [{
        "latitude": "...",
        "longitude": "...",
        "timestamp":"..."
        }],
        ...
} 

The nesting works fine until i save the complete track with track.save() to localStorage.
Then my PositionsCollection in memory turns into a blank Javascript-Array.
After saving I can't do something like track.get("positions"), which was possible before.

Saving the data to localStorage works. At initialization of my app I'm parsing the json-data like that:

parse: function(response) {
            _.each(response, function(track){
                  track.positions = new PositionsCollection(track.positions);
            });
            return response;
        }

After fetching the data from localStorage everything is fine again.

But I need to keep my positions in memory as a Backbone Collection as well.

Many thanks in advance for your help.
Best

Here is the output after logging each track to console (toJSON()):
bildschirmfoto 2014-05-12 um 16 36 35
track no.1 and track no.2 are parsed from localStorage. Track no.3 is the latest track after saving (without parsing again).

I've encountered the same problem.
The nested collection turns into a blank Javascript Array when localStorage serializes the model, calling JSON.stringify(model).

According to the manual:

If an object being stringified has a property named toJSON whose value is a function, then the toJSON() method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON() method when called will be serialized.

So when localStorage serializes the model, it calls model.toJSON(), the default Backbone toJSON, that it simply clones the model's attributes, excluding the nested collection.

A simple solution would be to change the behavior of the model toJSON() to include the nested collection.

I hope this can help you and future readers. 😄

@coire1, I can understand the issue. but can't fix yet. Can you help on this. I meet the same issue on my project
regards
Bao jingjing

Hi, it is really an old project but I found the piece of code you need.
In the model that contains the nested collection you have to redefine and override the toJSON method.
The new method has to initialize a new collection and copy the models in it.

toJSON: function() {
  /* Get the array as model attribute */
  var tempNestedModels = this.get('nestedCollection');
  if (!tempNestedModels) {
    /* if are present nested models initialize a new collection */
    nestedModels = new NestedCollection();
  }
  /* for each model in collection copy/filter the attributes needed */
  _.each(nestedModels.models, function(model) {
    model.attributes = _.pick(model.attributes, 'id', 'exampleAttr');
  });
  /* return the original model, filtering out unuseful attributes and adding
  the new collection instead of the original array */
  return _.merge(_.pick(this.attributes, 'id', 'modelAttr'), {
    nestedCollection: nestedModels
  });
}

Hi @coire1, do you happen to have the Backbone version for this? I've synchronised nested collections to the server in Backbone 1.3+ and it's all serialized correctly. If it's fixed upstream, I'll close this ticket.

Hello @scott-w, the issue for me happened with Backbone 1.1.2.
It is an old project and I can't test it with the newer version. Hope this helps anyway.
Let me know if I can do anything else.

Hi @coire1 thanks very much for that. I believe Backbone has resolved the issue since then so I'll close this and let anyone comment if they feel it needs to be looked at again.

Best regards,
Scott