I've got two async models:
App.Posts = DS.Model.extend({
'content': attr('string'),
'comments': DS.hasMany('comments', {async: true}),
});
App.Comments = DS.Model.extend({
'body': DS.attr('string'),
'postId': DS.belongsTo('posts', {async: true})
});
Via the PostController I try to load the Comments onClick through an action:
App.PostController = Ember.ArrayController.extend({
loadComments: function(post_id) {
this.store.find('comments', post_id);
}
});
(perhaps there is a better way to do this??)
Request and API Response are correct (see API Response below), but only one comment is rendered, then Ember throws an error:
TypeError: Cannot read property 'postId' of undefined
In Embers Console > Data Tab there is one comment in the comment Model, but there is also a post element in the comment model with the comment properties set to undefined. This would explain, why Ember can't read the property postId, because it's not a comment. Why does Ember push the post into the comment model and only pushes one instead of 3 comments into the model?
API Response
{
"comments": [
{
"id": 2,
"postId": 31152,
"body": "Lorem ipsum dolor sit amet, consetetur",
},
{
"id": 2,
"postId": 31152,
"body": "asdasd",
},
{
"id": 2,
"postId": 31152,
"body": "asd asd sd",
}
]
}
This is a slight shot in the dark, and I'd generally put it as a comment, but it's a tad big. Can you try changing all of the model references to singular. That's the correct pattern for Ember Data models.
App.Post = DS.Model.extend({
'content': attr('string'),
'comments': DS.hasMany('comment', {async: true}),
});
App.Comment = DS.Model.extend({
'body': DS.attr('string'),
'postId': DS.belongsTo('post', {async: true})
});
this.store.find('comment', post_id);
And now that I write this, I might see another issue. If you are querying for comments by post_id (pretend it's 7) then Ember Data is expecting a single record back, not a collection of records. So it's likely looking at the comments collection and thinking it's a single record, and that just blows up it's logic.
Related
DEBUG: Ember : 1.7.1
DEBUG: Ember Data : 1.0.0-beta.12
DEBUG: Handlebars : 1.1.2
DEBUG: jQuery : 1.10.2
Having an issue with what I believe is the belongsTo attribute on my user model. (This happens on my other belongsTo attributes within my application as well). I have a Django backend which returns a response when I comment out the network: attribute.
{
email: "test#test.com",
first_name: "Test",
global_code: "daht64q691zy4k887ch",
global_id: "GBID-USER-dat64q6917zy4k887ch",
institution_gbid: "GBID-GINS-567j53ey0lojsu2kys",
institution_name: "Some University",
last_name: "Testing",
network: { },
view_policy: {
capability: "system:view",
description: "Anyone can view a user",
hold: true,
id: "daht64q691y4k887ch:system:view",
values: ""
}
}
Code for the User Model:
App.User = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
global_id: DS.attr('string'),
network: DS.belongsTo('basicgrouping')
}):
Code for Basic Grouping model:
App.Basicgrouping = DS.Model.extend({
global_id: DS.attr('string'),
name: DS.attr('string'),
gbid_code: function(){
return getGBIDCode(this.get('global_id'));
}.property('global_id')
});
Debugging ember-data I placed a console.log() within the following code:
relationshipsByName: Ember.computed(function() {
var map = Map.create();
this.eachComputedProperty(function(name, meta) {
console.log(name, meta);
if (meta.isRelationship) {
meta.key = name;
var relationship = relationshipFromMeta(this.store, meta);
relationship.type = typeForRelationshipMeta(this.store, meta);
map.set(name, relationship);
}
});
This seems to show that the type of the object that it belongs to is not being found (Basicgrouping) as it's returning App.undefined.
My theory is it may have something to do when parsing the server response and maybe the payload response. This also happens in other belongTo relationships in my code.
It turns out that there was a file that was overriding some of the DS. methods and causing an empty type to be sent. I was in the process of removing use of the shim but didn't know that it was being used.
Thanks the Bmacs from the ember community for the help debugging the issue.
I am trying to do something that sounds simple but I can't find the solution.
My application needs to edit documents which contains pages.
Here is my model :
MyApplication.Document = DS.Model.extend({
title: DS.attr('string'),
pages: DS.hasMany('page', {async: true})
});
MyApplication.Page = DS.Model.extend({
document: DS.belongsTo('document', {async: true}),
title: DS.attr('string'),
params: DS.attr(),
objects: DS.attr()
});
And the routes :
MyApplication.Router.map(function () {
this.resource('document', {path: '/document/:document_id'});
});
MyApplication.Document = Ember.Route.extend({
model: function (params) {
return this.store.find('document', params.document_id);
}
});
When I load the document 1, the application call http://www.myserver.com/api/document/1.
The problem is that when I want to find a page of the document, it calls
http://www.myserver.com/api/pages/ID
instead of
http://www.myserver.com/api/document/1/pages/ID
Theses nested URL are important in my application.
I found different things on the subject like adding links in the JSON response :
{
"document": {
"id": "1",
"title": "Titre du document",
"pages": ["1", "2", "3"],
"links": {"pages" : "pages"}
},
But when I call for the pages, it requests http://www.myserver.com/api/document/1/pages without the id.
I also try specify the document when I ask for the page :
this.store.find("page", 1, {document:1});
Can't find a complete documentation on this subject, so if someone can explain me what's wrong, I'll be happy.
Thank.
Depends : EMBER DATA >= V1.0.0-BETA.9
The way to handle nested routes is hidden under release notes
Need to send back the links with response like this
{
"document": {
"id": 1,
"title": "Titre du document",
"links": {"pages" : "/documents/1/pages"}
}
You'll need to customize the adapter:page's buldUrl method like
MyApplication.PageAdapter = DS.RestAdapter.extend({
// NOTE: this is just a simple example, but you might actually need more customization when necessary
buildURL: function(type, id, snapshot) {
return '/documents/' + snapshot.record.get('document.id') + '/pages/' + id;
}
});
#code-jaff answer adapted to Ember 2.1.0:
// app/adapters/page.js
import DS from './application'; // I suppose you have your adapter/application.js
export default DS.RESTAdapter.extend({
buildURL: function(type, id, snapshot) {
return this.host + '/' + this.namespace + '/documents/' + snapshot.record.get('document.id') + '/pages/' + id;
}
});
Your problem likely stems from the quotes that are surrounding the IDs in your JSON. If you modify your serializer so that there are no quotes for the the IDs both around the document ID and the pages IDs, you should get the behavior that you expect. Also, you need to modify the formatting of your links to point to the relative path:
The resulting JSON should look like:
{
"document": {
"id": 1,
"title": "Titre du document",
"pages": [1, 2, 3],
"links": {"pages" : "/documents/1/pages"}
}
Please see this answer for a description of why adherence to Ember's expectations with regard to the JSON format is important and for an overview of the expected format.
While using the RESTAdapter, I have an Organization model which is to be embedded in the response. It appears that the default implementation of the Ember.RESTAddapter sends the id, using the same model name, but not as an object (this currently 'works'):
POST/PUT api/v1/item/{id}
{
"item" {
id: "1029383829"
...
organization: "26044097612186763401268824297"
}
}
I have consulted the documentation, and found that the mixin DS.EmbeddedRecordsMixin should do this, coupled with declaring embedded: "always" on the attrs or the Serializer:
models/item.js
var Item = DS.Model.extend({
...,
organization: DS.belongsTo("organization", {embedded: "always"})
});
serializers/item.js:
var ItemSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
organisation: {serialize: "id", embedded: "always"}
}
}
);
However, when deserializing records, Ember Data complains, saying that it expected an object, but just got a string:
Assertion Failed: Expected an object as data in a call to push for
app#model:organization: , but was 26044097612186763401268824297
Ultimately, I would prefer a system, likened to sideloading, wherein an additional attribute, post-fixed "_id", describes the corresponding id of an embedded record:
{
"item": {
id: 1,
name: "name",
organization_id: "26044097612186763401268824297"
...
}
}
How can I allow serializing and deserializing embedded id sideloading for my Organization model?
You aren't actually embedding the record, you're just specifying the id, in that case you should mark it as async.
var Item = DS.Model.extend({
...,
organization: DS.belongsTo("organization", {async: true})
});
And remove your embedded records implementation.
Considering something similar to the example outlined here:
App.Router.map(function() {
this.resource("posts", function() {
this.resource("post", { path: "/posts/:post_id" }, function() {
this.resource("comments", { path: "/comments" });
});
});
});
using the DS.RESTAdapter. The Router would load all the posts when I access the PostsRoute with a call to the API URL /posts.
When I then access PostRoute, for example for the post with id=1, it doesn't hit the API again, i.e. it doesn't hit /post/1. It loads the post from the store.
I want then to access CommentsRoute for post with id=1. How do I load the comments?
Should I have sideloaded all the comments in the first place, when I loaded the post list? In this case though, I would need to load all the comments for all the posts. Is it possible to load the comments only when needed, i.e. when I access CommentsRoute?
In this case, how do I actually load the comments from the backend?
Specifically, how do I write the CommentsRoute to load the comments from the RESTful API when I access the page that actually displays them?
I guess one needs to have the following:
App.Post = DS.Model.extend({
comments: DS.hasMany('comment')
});
App.Comment = DS.Model.extend({
post: DS.belongsTo('post')
});
App.CommentsRoute = Ember.Route.extend({
model: function() {
/*
* How do I inject params.post_id here
* to make a request to the RESTful API?
* Which URL would be called?
* /comments?post_id=ID
* /post/post_id/comments
* ...
*/
// Doesn't work, hits /comments
return this.store.find('comment', { post_id: params.post_id });
}
});
Why does
return this.store.find('comment', { post_id: params.post_id });
hit /comments?
You just need to declare your CommentsRoute like this:
App.CommentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('post').get('comments');
}
});
What it does is, it gets the model of the PostRoute and fetches its comments.
Ember-data handles the logic behind it. If comments were sideloaded, it will just return these. Otherwise it will issue a GET request to fetch them.
For this to work, you need to include the links property on a serialized post. The links property needs to include the URL you wish ember-data to use in order to fetch the comments.
E.g. your serialized post may look like this:
{
"post": {
"id": 1,
"title": "Rails is omakase",
"links": { "comments": "/posts/1/comments" }
}
}
See DS.RESTAdapter#findHasMany.
The hasMany relationship probably needs to be declared async for this to work properly:
App.Post = DS.Model.extend({
comments: DS.hasMany('comment', { async: true })
});
You can use Ember's sideloaded relationships to make the posts API endpoint also return the relevant comments and Ember will figure it out.
http://emberjs.com/guides/models/the-rest-adapter/#toc_sideloaded-relationships
{
"post": {
"id": 1,
"title": "Node is not omakase",
"comments": [1, 2, 3]
},
"comments": [{
"id": 1,
"body": "But is it _lightweight_ omakase?"
},
{
"id": 2,
"body": "I for one welcome our new omakase overlords"
},
{
"id": 3,
"body": "Put me on the fast track to a delicious dinner"
}]
}
You'd then be able to pass the already loaded comments to the comments route.
It may be in the docs but it's quite a specific term! Some of the concepts like that can be a bit tricky to search for.
The following forces a call to the backend /comments?post_id=ID
App.CommentsController = Ember.ArrayController.extend({
needs: 'post'
});
App.CommentsRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('comment', { post_id: this.modelFor('post').get('id') });
}
});
Using Ember-data and Ember.js, I'm trying to load two models with one JSON request. The models have a relationship analogous to this:
App.Person = DS.Model.extend({
name: DS.attr('string'),
dogs: DS.hasMany('App.Dog'),
});
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person'),
});
My server is sending JSON like this:
{
"dog": {
"id": 1,
"name": "Fido",
"owner": {
"id": 1,
"name": "John Smith",
"dogs": [1]
}
}
}
…And yet, Ember-data still sends a request (using findQuery) to my server trying to get the owner JSON.
I have a jsFiddle set up that demonstrates it here. To watch the problem happen, you'll need to go to this link to activate the route/template:
http://fiddle.jshell.net/6kQ8s/2/show/#/dog/1
I haven't defined findQuery() in my adapter on purpose because I shouldn't need that to get data that I have already sent… Right?
Does anyone know what I'm doing wrong here?
I'm doing the following (using ember-data revision 8)
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person', { embedded: true }),
});
Additionally, I have to tell the serializer to load a mapping for this relation.
Though it's not required, I'm using my own DS.Serializer subclass. At initialisation
time the serializer loads a mapping for the Person class, which specifies that
embedded relationships should be loaded.
App.WOSerializer = DS.Serializer.extend({
init: function(){
this._super();
this.map(App.Dog, {
person: {
embedded: 'load'
}
});
});
Edit by question asker:
The serializer needed to be initialized in the adapter.
App.adapter = DS.Adapter.create({
// ...
serializer: App.WOSerializer.create()
});
Try use embedded property.
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person', { embedded: true }),
});