dhruvaray / backbone-associations

Create object hierarchies with Backbone models; Respond to hierarchy changes using regular Backbone events.
http://dhruvaray.github.io/backbone-associations/
MIT License
492 stars 72 forks source link

Reverse relations not working when populating object from JSON #150

Open rickgn opened 10 years ago

rickgn commented 10 years ago

I'm not sure if this is a question, but or feature request. I have been using BackboneRelational for about a year now. I frequently deeply nested relations with reverse relations. I wanted to try Associations due to many cool features over Relational but I hit a brick wall when I encountered this one:

If I have some JSON:

{
  "parent": {
    "id": 123
    "name": "TheParent"
    "children": [
      { "id": 456, "name": "TheChild"}
    ]
  }
}

To configure this I would use:

var ParentModel = Backbone.AssociatedModel.extend{ 
relations: [
  {  key: "children", type:Backbone.Many, relatedModel: 'ChildModel'  }
] }

var ChildModel = Backbone.AssociatedModel.extend{ 
relations: [
  {   key: "parent", type:Backbone.One, relatedModel: 'ParentModel'  }
] }

var myModel = new ParentModel( myJSON ); // using JSON from above

So far so good. I can run Mocha+Chai tests and verify that relations are created correctly

it("should have the correct class type in the child model", function(){
  expect(mymodel.get('children[0]') instanceof ChildModel).to.be.true;
})
// pass.

Great. Now here's the issue:

it("should have the correct class type in the child model", function(){
  var childobj = mymodel.get('children[0]');
  var reverseObj = childobj.get('parent');   // --> undefined
  expect(reverseObj instanceof ParentModel).to.be.true;
})
// FAILS!.   

Basically the issue here is that I cannot automatically create the reverse relations. Backbone-Relational does this if you define type: hasMany on the parent and type: hasOne on the child. It seems that in Associations I have to explicitly set the relationship with the actual classes as in:

parentObj.get("children").add( new ChildModel { parent: parentObj }

Am I missing something in the documentation? I read it like 5 times and can't find a way to do this. So is this a question, a bug or a feature request?

jameslai commented 10 years ago

I believe you can accomplish your goal without worrying about starting the parent/child relationship in your ChildModel (leave it basically empty). You can access the child's parent via childobj.collection.parents.

rickgn commented 10 years ago

Thanks for your reply. I looked at that as one potential option. Question: Is this a reliable way for the associations to work? Are there any cases where it might not?

However, this solution means custom code / functions every time when what I really wanted was simple bi-directional relations. I suppose I could bake this into my own abstract class that extends AssociatedModel:

 getParent: function(){  
   var collection = this.collection; 
   if(! collection return null);
   var parents = collection.parents;
   if(! parents) return null;
   return parents[0] 
} 

But then by doing so, wouldn't we be better off having that type of function in a reverse relation definition?

For example, in Backbone.Relational it looks like this:

relations:[
    {
        key:'children',
        type: Backbone.HasMany,
        relatedModel: "ChildModel",
        reverseRelation: {
            key: 'parent',
            includeInJSON: false,
            type: Backbone.HasOne
        }
    }
]

In that example, the reverse relation is defined in the parent object. I believe this is so that when the parent object creates the relation it also creates the reverse relation at the same time.

My suggestion would be to have a similar pattern in the Associations parent object. I don't know Associations well enough to make that commit at this point so I'm wondering if someone is willing to take that on. Due to release deadlines I don't have the bandwidth to experiment for a while.