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.
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'));
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'}) });
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]
}
}
Related
I've got a very basic Price model that looks like this:
App.Price = DS.Model.extend({
value: DS.attr()
});
App.Price.reopenClass({
FIXTURES: [
{ id: 1, value: 29.99 },
{ id: 2, value: 39.99 },
{ id: 3, value: 49.99 },
{ id: 4, value: 55.99 }
]
});
Here's the route that's using this model:
App.PricingRoute = Ember.Route.extend({
model: function(){
return this.store.find('price');
}
});
In the controller, I set the sorting to be based on the value attribute:
App.PricingController = Ember.ArrayController.extend({
sortProperties: ['value'],
sortAscending: true
});
Then my template (in Jade) where I want them to be display in sorted order:
{{#each price in this}}
li.price-levels__value {{price.value}}
.price-levels__remove("{{action 'delete' price.id}}")
{{/each}}
Problem is they're not sorted. Interesting fact is that if I change the type of the value attribute to strings, the sorting DOES work.
eg.
{ id: 1, value: '29.99' }
{ id: 2, value: '39.99' }
etc.
So how do I get the sorting to work on a numerical model attribute?
I'm really struggling to understand how polymorphic relationships worm in Ember Data (Beta 11) and cannot find any update information on how to set them up and what is expected in the JSON payload. I'm trying to create a feed of items (think facebook feed) where you have different types of items in the feed. My modeling looks something like the following.
App.Feedable = DS.Model.extend({
activities: DS.hasMany('activity')
});
App.Activity = DS.Model.extend({
feedable: DS.belongsTo('feedable', { polymorphic: true, async: false })
});
App.MemberLikeShare = DS.Model.extend({
status: DS.attr('string')
});
App.PhotoShare = DS.Model.extend({
status: DS.attr('string'),
photo: DS.attr('string')
});
When I do a fetch at /activities I send back JSON that looks like the following:
{
activities: [
{
id: 1,
feedable: { id: 1, type: 'memberLikeShare' }
},
{
id: 4,
feedable: { id: 4, type: 'memberLikeShare' }
},
{
id: 5,
feedable: { id: 5, type: 'photoShare' }
}
],
member_like_shares: [
{
id: 1,
status: 'Foo'
},
{
id: 4,
status: 'Bar'
}
],
photo_shares: [
{id: 5, photo: 'example.jpg'}
]
}
When this runs I get an error like:
You can only add a 'feedable' record to this relationship Error: Assertion Failed: You can only add a 'feedable' record to this relationship
I'm assuming my relationships are wrong or I'm sending the wrong JSON?
polymorphic relationships should extend the base type.
App.Feedable = DS.Model.extend({
activities: DS.hasMany('activity')
});
App.MemberLikeShare = App.Feedable.extend({
status: DS.attr('string')
});
App.PhotoShare = App.Feedable.extend({
status: DS.attr('string'),
photo: DS.attr('string')
});
I'd also expect them to define the activities on them.
member_like_shares: [
{
id: 1,
status: 'Foo',
activites: [1,2,3,4]
},
{
id: 4,
status: 'Bar',
activites: [1,2,3,4]
}
],
photo_shares: [
{
id: 5,
photo: 'example.jpg',
activites: [1,2,3,4]
}
]
Doing a basic update to an Ember Fixture record using the new "needs" helper instead of the old "controllerFor". Here is a distilled version of my code:
App.Item = DS.Model.extend({
utensil: DS.belongsTo('App.Utensil')
});
App.Utensil = DS.Model.extend({
name: DS.attr('string')
});
App.Item.FIXTURES = [
{id: 0, utensil: 2},
{id: 1, utensil: 1}
];
App.Utensil.FIXTURES = [
{id: 0, name: 'test1'},
{id: 1, name: 'test2'},
{id: 2, name: 'test3'},
{id: 3, name: 'test4'}
];
App.UtensilController = Ember.ArrayController.extend({
needs: ["item"],
setUtensil: function(utensil) {
var item_controller = this.get('controllers.item');
item_controller.get('model').set('utensil', utensil);
}
});
It seems that the item model is coming back empty. Is this is a fixture limitation or am I doing something wrong?
I'm trying to figure out how to populate a table from a JSON object.
My JSON is a structurated object:
{
id: 0,
list: [{ username:'user_1',online:true, user:0 },
{ username:'user_2',online:true, user:0 }]
}
My Model is defined as follow:
MyTalk.WUser = DS.Model.extend({
list: DS.hasMany('MyTalk.User')
});
MyTalk.User = DS.Model.extend({
username: DS.attr('string'), // primary key
online: DS.attr('boolean'),
user: DS.belongsTo('MyTalk.WUser')
});
I am using a custom Adapter for ember-data:
DS.SocketAdapter = DS.RESTAdapter.extend(MyTalk.WebSocketConnection, {
// code not relevant
}
DS.SocketAdapter.map('MyTalk.WUser', {
list: {embedded: 'always'}
});
DS.SocketAdapter.map('MyTalk.User', {
primaryKey: 'username'
});
MyTalk.Store = DS.Store.extend({
revision: 12,
adapter: DS.SocketAdapter.create({})
});
Now I would load my data. I run in Chrome command line the following statements:
var store = DS.get('defaultStore');
var obj = {
id: 0,
list: [{ username:'user_1',online:true, user:0 },
{ username:'user_2',online:true, user:0 }]
};
var store.loadMany(MyTalk.WUser,obj);
var record = MyTalk.WUser.find(0);
record.serialize();
But it returns no record:
> Object {list: Array[0]}
thanks in advance!!
If you want to allow the adapter to deserialize embedded records (or perform any custom deserialization, for that matter), you'll need to load your data through the adapter rather than directly into the store.
var store = DS.get('defaultStore'),
obj = {
id: 0,
list: [{ username:'user_1', online:true, user:0 },
{ username:'user_2', online:true, user:0 }]
},
type = MyTalk.WUser,
adapter = store.adapterForType(type);
adapter.load(store, type, obj);
I have an ember-data model definition that looks like this:
Sylvius.Filter = DS.Model.extend({
title: DS.attr('string'),
slug: DS.attr('string'),
// Belongs to Atlas
atlas: DS.belongsTo('Sylvius.Atlas'),
// Has images
images: DS.hasMany('Sylvius.Image'),
// May have AtlasExtras
extras: DS.hasMany('Sylvius.AtlasExtra'),
// Structures for this filter
structures: DS.hasMany('Sylvius.Structure'),
// This is the path to the thumbnails sprite.
// Each image will have an index on this sprite
thumbnailUrl: DS.attr('string'),
// How big is each thumbnail?
thumbnailHeight: DS.attr('number'),
thumbnailWidth: DS.attr('number'),
// How big are the images?
imageHeight: DS.attr('number'),
// which image is selected?
selectedImage: DS.belongsTo('Sylvius.Image')
});
I have an ember-data fixture-adapter store set up like this:
Sylvius.fixtureStore = DS.Store.create({
revision: 4,
adapter: DS.fixtureAdapter
});
...and fixtures which look like this:
Sylvius.Filter.FIXTURES = [{
"id": 1,
"title": "Unlabeled",
"slug": "unlabeled",
"thumbnailUrl": "assets/img/surface_anatomy/photographic/srf-photo-unlabeled-tn.gif",
"thumbnailWidth": 100,
"thumbnailHeight": 75,
"atlas_id": 1,
"images": [1, 2, 3, 4, 5, 6, 7],
"structures": [0]
}];
(All this code is in this jsfiddle which demonstrates the problem.)
Here's the issue: the title is accessible just fine. The slug is also there. The thumbnailUrl, thumbnailWidth, thumbnailHeight, are all undefined. Why?
You are not following ember-data's rails centric naming conventions. You can either change your fixture data to:
{
"id": 1,
"title": "Dummy Title",
"slug": "dummy-title",
"thumbnail_url": "path/to/thumbnail.gif",
"thumbnail_width": 100,
"thumbnail_height": 75,
"atlas_id": 1,
"images": [1, 2, 3, 4, 5, 6, 7],
"structures": [0]
}
or change your mapping to include a key:
thumbnailUrl: DS.attr('string', { key: 'thumbnailUrl' }),
thumbnailHeight: DS.attr('number', { key: 'thumbnailHeight' }),
thumbnailWidth: DS.attr('number', { key: 'thumbnailWidth' })