Backbone-relational icon indicating copy to clipboard operation
Backbone-relational copied to clipboard

Parse data of a hasMany relationship by its associated collection

Open thomaswolf opened this issue 10 years ago • 2 comments

It would be great if models in a hasMany relationship could be parsed by the corresponding collection model. Here is an example:

var MyModel = Backbone.Model.extend({
 relations : [
  type: Backbone.HasMany,
  key : 'hasManyRelation',
  relatedModel : 'MyModel',
  collectionType : 'MyModelCollection',
  parseCollection : true // <--- unfortunately this does not exist
 ],...
});
...
var MyModelCollection = Backbone.Collection.extend({
 parse: function (resp) {
  this.metadata = resp.metadata;
  return resp.items;
 },
)}

The data returned by the server:

{
 metadata : { ... }
 items : {
  id : 1,
  attribute : "123",
  hasManyRelation : {
    metadata : { ... }
    items : [...]
   }
 }
}

thomaswolf avatar Apr 12 '14 02:04 thomaswolf

I'm having the same issue. My use case is that my collections return data as paged collections. The actual array is a "data" property of the returned data.

The response for the object with a collection of children looks like:

{ 
  /* model properties */
  "children":
  {
    "paged": false, 
    "rowCount": null,
    "hasMore": false,
    "maxRows": null, 
    "offset": 0,
    "data": [ /* models are here */ ]
  }
}

My collection has a parse method:

          parse: function(response)
          {
            if (response["data"] != null)
            {
              var t = this;
              $.each(["offset", "maxRows", "hasMore", "paged"],
                function(i, prop)
                {
                  t[prop] = response[prop];
                });

              this._loadedPages[Math.floor(this.offset / this.maxRows)] = true;

              if (this.hasMore == false && Number.isInteger(this.rowCount) == false &&
                response.data.length)
              {
                this.rowCount = this.offset + response.data.length;
              }
              else if (Number.isInteger(response["rowCount"]))
              {
                this.rowCount = response.rowCount;
              }

              return response["data"];
            }

            return response;
          },

The problem is that when this collection is loaded as a relation, the parse method is never called. In this use case I don't care about having parse called on the model, I only need it called on the collection.

arobinson avatar Aug 21 '14 22:08 arobinson

Found a work-around for those that may want it.

In your model (not the collection), implement a parse method:

      parse: function(response, options)
      {
        var children = response.children;
        if (children && children.data)
        {
          collection = new CustomCollection();
          items = collection.parse(children);
          collection.reset(items, { silent: true });
          response.children = collection;
        }

        return response;
      },

The backbone-relational code already checks to see if the items are a Backbone collection before parsing them in fetchRelated, so by pre-parsing them it avoids the issue.

Not pretty but it does work around the issue.

arobinson avatar Aug 21 '14 23:08 arobinson