11

Has anyone used ember-data to model a tree of data?

I would assume it would be something like:

Node = DS.Model.extend({
    children: DS.hasMany(Node),
    parent:   DS.belongsTo(Node)
});

However, I have not been able to get this working which leads be to believe that either: 1) I'm just plain wrong in how I'm setting this up or, 2) it is not currently possible to model a tree using ember-data.

I'm hoping that it's the former and not the latter...

Of course it could be the JSON...I'm assuming the JSON should be of the form:

{
    nodes: [
        { id: 1, children_ids: [2,3], parent_id: null },
        { id: 2, children_ids: [], parent_id: 1 },
        { id: 3, children_ids: [], parent_id: 1 }
    ]
}

Any tips/advice for this problem would be greatly appreciated.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Heuristocrat
  • 163
  • 1
  • 6
  • What doesn't work exactly? Could you please add some code to your question to show us the problem? – Stéphane Blond Aug 20 '12 at 16:18
  • Sure, I set up a simple [jsFiddle example](http://jsfiddle.net/heuristocrat/5aqHx/) that shows how I believe I should be defining the model and then doing a search for a root node and attempting to access the children (which is where things break down). – Heuristocrat Aug 20 '12 at 16:59
  • That's a useful fiddle. I've had similar issues myself in a non-recursive hasMany, so I'm interested in seeing any solutions. – pjmorse Aug 20 '12 at 18:17

3 Answers3

14

There are several little things that prevent your fiddle to work:

  • the DS.hasMany function asks for a String as argument. Don't forget the quotes: DS.hasMany('Node')

  • in the fixture definition, hasMany relationships should not be postfixed by _ids or anything. Just use the plain name. For instance: { id: 42, children: [2,3], parent_id: 17 }

  • the length property of DS.ManyArray should be accessed using the get function: root.get('children.length')

  • by default, the fixture adapter simulates an ajax call. The find query will populate the record after waiting for 50ms. In your fiddle, the root.get('children.length') call comes too early. You can configure the fixture adapter so that it makes synchronous call:

    App.store = DS.Store.create({
        revision: 4,
        adapter: DS.FixtureAdapter.create({
            simulateRemoteResponse: false
        })
    });
    

    Or you can load data to the store without any adapter:

    App.store.loadMany(App.Node, [
        { id: 1, children: [2, 3] },
        { id: 2, children: [], parent_id: 1 },
        { id: 3, children: [], parent_id: 1 }
    ]);
    
  • and last one: it seems like the Ember app should be declared in the global scope (no var), and Ember-data models should be declared in the app scope (replacing var Node = ... by App.Node = ...)

Full example:

App = Ember.Application.create();

App.store = DS.Store.create({
    revision: 4
});

App.Node = DS.Model.extend({
    children: DS.hasMany('App.Node'),
    parent:   DS.belongsTo('App.Node')
});

App.store.loadMany(App.Node, [
    { id: 1, children: [2, 3] },
    { id: 2, children: [], parent_id: 1 },
    { id: 3, children: [], parent_id: 1 }
]);

var root = App.store.find(App.Node, 1);

alert(root.get('children'));
alert(root.get('children.length'));
Stéphane Blond
  • 810
  • 10
  • 18
  • assuming App.Node has an attribute "name", is it possible to iterate over all children in a template like this: {{#each child in controller.children}}{{child.name}}{{/each}} ? – lipp Nov 02 '12 at 13:01
  • How would you delete a Node without encountering an infinite recursion? I'm currently facing this problem. – Tim O Feb 25 '13 at 05:21
1

This didn't work for me until I set up the inverse:

App.Node = DS.Model.extend({
    children: DS.hasMany('App.Node', {inverse: 'parent'}),
    parent:   DS.belongsTo('App.Node', {inverse: 'children'}) });
Steven Soroka
  • 19,404
  • 4
  • 52
  • 40
  • 1
    It doesn't seem to work in ember-data 1.0.0-beta.7 (last version). Did anyone resolved this in the lastest versions? – Gorzas Apr 14 '14 at 13:40
-3

Not sure but as per example given in ember guide

App.Post = DS.Model.extend({
  comments: DS.hasMany('App.Comment')
});

The JSON should encode the relationship as an array of IDs:

{
  "post": {
    "comment_ids": [1, 2, 3]
  }
}
Swapnil
  • 265
  • 5
  • 10