I'm trying to understand using Ember's hasMany. In particular, I'd like to be able to grab a particular object(s). I've tried grabbing firstObject on it but that doesn't work. And I've also tried looping over each object.
jsBin and jsBin with looping
Important code:
App.MyModel = DS.Model.extend({
name: DS.attr('string'),
myOthers: DS.hasMany('App.MyOtherModel')
});
DS.RESTAdapter.map('App.MyModel',{
myOthers: { embedded: 'always' }
});
App.MyOtherModel = DS.Model.extend({
name: DS.attr('string')
});
App.store.load(App.MyModel, {
id: 2,
name: "myModel",
my_others: [
{ name: 'myOther1' },
{ name: 'myOther1' }
]
});
console.log(myModel.get("myOthers.firstObject.name"));
I'm trying to do this for my tests, but I'm not having any luck.
How would I work with a hasMany relationship in order to grab a particular object and be able to loop over them? Thanks.
I found out that you have to go through the adapter, not the store.
updated jsbin
App.adapter = DS.RESTAdapter.create();
App.adapter.load(App.store, App.MyModel, {
id: 2,
name: "myModel",
my_others: [
{ name: 'myOther1' },
{ name: 'myOther2' }
]
});
Related
I'm trying to set up the attributes to my model but having trouble when nesting objects. Here is what I have
App.Posts = DS.Model.extend({
title: DS.attr('string'),
author: {
name: DS.attr('string')
},
date: DS.attr('date'),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
How am I suppose to declare the author object?
In the ember inspector when I go under data and under App.post and select one of the rows. It has a property a property App.Post with an attribute author: { name: [Object] }
here is the JS Bin link http://jsbin.com/tesozepexaqi/2/
Ember works perfectly fine without Ember Data. Let's pretend we want to do it with Ember Data:
Ember Data's Records should be flat. This means all properties are at the top level. If you have a related data that exist deeper they generally live in a different record. If you're attempting to embed the record you'll need to look into the tricky world of embedded records (Ember-data embedded records current state?). At the very least these related records must have an id defined. So here's an example of what the data returned from server should look like.
{
posts:[
{
id: '1',
title: 'My name is Django.',
author: {
id:1,
name: 'D name'
},
date: new Date('08-15-2014'),
excerpt: 'The D is silent.',
body: 'The D is silent.'
},
{
id: '2',
title: 'White horse',
author: {
id:2,
name: 'horse name'
},
date: new Date('08-15-2014'),
excerpt: 'Is what I ride.',
body: 'My horse likes to dance.'
}
]
}
Code
App.ApplicationAdapter = DS.RESTAdapter.extend();
App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
author: {embedded: 'always'}
}
});
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.belongsTo('author'),
date: DS.attr('date'),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
App.Author = DS.Model.extend({
name: DS.attr()
});
Example: http://jsbin.com/vazada/1/edit
One other small tip, you'll not want to use globals when working with the routes, you can use modelFor to get the model from a different route.
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
}
});
App.PostRoute = Ember.Route.extend({
model: function(params) {
var posts = this.modelFor('posts');
return posts.findBy('id', params.post_id);
}
});
Personally, I think Ember Data is overkill. Ember works perfectly well with POJOs. If you need caching and the ability to rollback then Ember Data might be a good solution for you.
Example: http://jsbin.com/vazada/2/edit
Adjusting this example from the Ember docs like the below should work:
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.belongsTo('author'),
date: DS.attr('date'),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
App.Author = DS.Model.extend({
post: DS.belongsTo('post')
})
One of the powers of Ember Data is that it uses identity maps to ensure that there is a single source of truth, but to ensure that this happens, it's very prescriptive on how the models need to be configured and what the server needs to return. But sometimes in a mostly read-only app, the single source of truth is not necessary and changing the API on the server is more work than necessary. In these instances, is there a way to tell Ember Data to just accept an array or object as is and don't try to map it to an identity map? Here's a quick example using widget and category:
This is what Ember Data wants us to do:
App.Widget = DS.Model.extend({
name: DS.attr('string'),
categories: DS.hasMany('category')
});
App.Category = DS.Model.extend({
name: DS.attr('string')
});
And Ember Data wants us to return this from the server:
{
"widget": {
"name":"Awesome Device!",
"categories":[1,2]
},
"categories":[
{id:1,"name":"Device"},
{id:2,"name":"Fun"}
]
}
But if this is a mostly a read-only app where the categories won't change, is there a way to tell Ember Data to treat categories as just simple value objects?
App.Widget = DS.Model.extend({
name: DS.attr('string'),
categories: DS.attr('array') // any way to do something like this?
});
Then the server can simply return this:
{
"widget": {
"name":"Awesome Device!",
"categories": [
{ id: 1, "name": "Device" },
{ id: 2, "name": "Fun" }
]
}
}
You can create your own transform, some common ones that you might attempt are raw, or array. I generally gravitate toward raw when I'm lazy
App.RawTransform = DS.Transform.extend({
deserialize: function(serialized) {
return serialized;
},
serialize: function(deserialized) {
return deserialized;
}
});
App.register("transform:raw", App.RawTransform);
App.Widget = DS.Model.extend({
name: DS.attr('string'),
categories: DS.attr('raw')
});
http://emberjs.com/api/data/classes/DS.Transform.html
I have two models:
App.Offer = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
products: DS.hasMany('product')
});
App.Product = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
offer: DS.belongsTo('offer')
});
And the server is answering with record and array of ids in this way (for example if the rest adapter asks for /offers/1):
{ "offer": [ { "id": 1, "name": "aaaaaaaaa", "description": "aaaa", "product_ids": [ 1, 2 ] } ] }
but now how can I get the products? I have a route like this:
App.OffersRoute = Ember.Route.extend({
model: function() {
var offer = this.get('store').find('offer', 1);
return offer
}
});
In Ember guide is written that if you want the products you should do:
offer.get('products');
Ok, but where should I put this? in the model hook? in a Controller property?
I've tried many things but I can see no network request to products?id[]=1&id[]=2 as I expected (the server is responding correctly to this request);
Can someone please give an example showing how I can find an offer, its products and use this data in my template?
If you're using the RESTAdapter your data needs to be in this format (if you don't want to return it in this format you can create a custom serializer and fix up the json).
2 differences:
the item under offer shouldn't be an array, since you were looking for a single item it should be an object
the key product_ids should be products, product_ids is the format that the ActiveModelAdapter/ActiveModelSerializer use.
JSON
{
"offer":
{
"id":1,
"name":"aaaaaaaaa",
"description":"aaaa",
"products":[
1,
2
]
}
}
The hasMany relationship should be marked as async if you're expecting it to be returned in a separate payload.
App.Offer = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
products: DS.hasMany('product', {async:true})
});
I hooked it up in jsbin below, but I didn't hook up a result from products?ids[]=1&ids[]=2 (note ids[]=, not id[]=), if you check the network tab you'll see the request being issued (but it'll crash since there is no result).
http://emberjs.jsbin.com/OxIDiVU/345/edit
I'm having a ton of trouble with updating a model in my Ember application. I can't seem to find good documentation that describes how to update my app. The following code is what I'm trying. This sends an update to /playlists/:playlist_id, unfortunately it doesn't send the updated songs as well... is there some callback for pushObject that I can't find? Am I trying trying to save the wrong thing?
App.PlaylistIndexController = Ember.ObjectController.extend({
actions: {
addSong: function(song) {
songs = this.get('songs');
songs.pushObject(song);
this.get('model').save();
}
}
});
App.Playlist = DS.Model.extend({
name: DS.attr('string'),
songs: DS.hasMany('song'),
});
App.Song = DS.Model.extend({
name: DS.attr('string'),
artist: DS.attr('string'),
playlist: DS.belongsTo('playlist'),
});
I am working on an application using ember.js and a couch DB backend. So far, i used ember-resource as database driver, but I am considering switching to ember-data, since this seems to be more sustainable.
Since I am working with couch DB, I am using the Couch DB-Adapter.
The documents in my database contain complete object structures, so I have to specify embedded objects in the database driver.
But although I am specifying my sub-objects as embedded, ember-data seems to fetch these objects with separate requests, instead of just getting them out of the main json.
My object definitions are as follows:
App.UserProfile = DS.Model.extend({
type: DS.attr('string'),
fullname: DS.attr('string'),
email: DS.attr('string'),
pictureUrl: DS.attr('string'),
social: DS.hasMany('App.SocialWebAccount', { embedded: true }),
.....
});
App.SocialWebAccount = DS.Model.extend({
profile: DS.belongsTo('CaiMan.UserProfile'),
site: DS.attr('string'),
account: DS.attr('string'),
.....
});
and the server data ist something like this:
{
"_id": "thoherr",
"_rev": "55-d4abcb745b42fe61f1a2f3b31c461cce",
"type": "UserProfile",
"fullname": "Thomas Herrmann",
"email": "test#thoherr.de",
"pictureUrl": "",
"social": [
{
"site": "socialFacebook",
"account": "thoherr"
},
{
"site": "socialXing",
"account": "Thomas_Herrmann7"
},
{
"site": "socialEmail",
"account": "test#thoherr.de"
}
]
}
After loading, the UserProfile does contain an ArrayProxy for my social data, which is populated by three entries, but they are all undefined instead of instances of SocialWebAccount!
If i try to access this array, ember-data seems to do a separate database access to fetch the data, which then leads to an error because the couch DB-adapter accesses an _id field, which is not available in undefined....
What am i missing?
I thought the "embedded" flag signals that the data is already in the json and the objects can be instantiated from the json?
Why does ember-data try to fetch the embedded data?
Any hint?
It seems that the embedded option has changed recently. I found some information in the test files on the ember-data github.
In these test files, the embedded content is defined like this
Comment = App.Comment = DS.Model.extend({
title: attr('string'),
user: DS.belongsTo(User)
});
Post = App.Post = DS.Model.extend({
title: attr('string'),
comments: DS.hasMany(Comment)
});
Adapter = DS.RESTAdapter.extend();
Adapter.map(Comment, {
user: { embedded: 'always' }
});
or
Adapter = DS.RESTAdapter.extend();
Adapter.map(Comment, {
user: { embedded: 'load' }
});
'always' seems to be used for embedded data without ids (your case),eg
id: 1,
title: "Why not use a more lightweight solution?",
user: {
name: "mongodb_expert"
}
'load' seems to be used for embedded data with ids
id: 1,
user: {
id: 2,
name: "Yehuda Katz"
}
Hoping it will help in your particular case. I've had a lot of trouble with hasMany relationships recently (I had to modify my adapter to get it work)