Ember children defined by a hasMany relationship suddenly change to embedded model - ember.js

I have the following model defined:
App.Post = DS.Model.extend({
title: DS.attr('string'),
comments: DS.hasMany('comment')
});
App.Comment = DS.Model.extend({
message: DS.attr('string')
});
If I create a Post entry with a Comment, the JSON stored in my browsers local storage references the Comments as an array of IDs which works great:
...
"o3duh":{
"id":"o3duh",
"title":"How to write Ember",
"comments":[
"jf0a2"
]
}
...
However, the moment I add another Post, the JSON suddenly changes such that Comments are embedded:
...
"o3duh":{
"id":"o3duh",
"title":"How to write Ember",
"comments":[
{
"message":"First!"
}
]
},
"6kudl":{
"id":"6kudl",
"title":"Learning Ember is painful",
"comments":[
]
}
...
Why is this happening? Can I prevent it? This is causing me problems because once it changes into this embedded format, the data cannot be read by the LSAdapter when reloading the page.
Here is a JSBin so you can see it happen for yourself and see the full JSON etc. To reproduce the problem, just create a post and add a comment then you can refresh the page without problem. Then add another post and try to refresh the page.
I'm not sure if the problem is with ember-data or the localstorage adapter.

I solved the problem by modifying the LocalStorageAdapter so that it only attempts to persist JSON in the expected format.
You can see the pull request I submitted to the original author here: https://github.com/rpflorence/ember-localstorage-adapter/pull/26
Hopefully it will either get folded into the LSAdapter project, or better still, someone will come up with a better solution ;)

I was able to fix the JSON issue by defining the inverse relationship on Comment:
App.Comment = DS.Model.extend({
message: DS.attr('string'),
post: DS.belongsTo('post')
});
There are new issues now, but hopefully this will help.

Related

Ember {embedded: 'always' } on model Vs Serializer

I was reading through Ember docs and some examples on working with Embedded Object like JSON in Ember.
I came across the EmbeddedRecordsMixin feature and saw that we can write code like below to tell it is embedded record.
import DS from 'ember-data';
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
author: { embedded: 'always' },
}
});
Qouting the below from Ember page
Note that this use of { embedded: 'always' } is unrelated to the { embedded: 'always' } that is defined as an option on DS.attr as part of defining a model while working with the ActiveModelSerializer. Nevertheless, using { embedded: 'always' } as an option to DS.attr is not a valid way to setup embedded records.
And i have also seen model written like this.
App.Child = DS.Model.extend({
name: DS.attr('string'),
toys: DS.hasMany('toy', {embedded: 'always'}),
});
Where child object has toys object embedded.
Going by the first example, can i write the child serailizer as below?
App.ChildSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
toys: {embedded: 'always'}
}
});
Can someone help me understand the difference between these two {embedded: 'always'} and what to use when?
Thanks
Short answer: yes you can and you should.
Well, as far as I know, ember (especialy ember-data) is build to work perfectly with a Rails backEnd.
Rails have a module called ActiveModelSerializer to serialize resources and their related attributes and relationships. Into this module, you can use an option embedded: 'always' to serialize the whole targeted relationship and not only the ids when your client ask for a ressource.
If you use it Rails side (server), you can handle it Ember side (client) by putting this option into your model if you want your ember-data store to handle it easily. It's just an 'echo' to this ActiveModelSerializer module functionality.
On the other side, if for example you create/update an object with many relationships, there is 2 ways to deal with it. The first is to first save object's relationships and then, on success, save the object itself. The second is to send it at once to your server with the option {embedded: 'always'} into your model's serializer, into the relationship you want to send at the same time (embedded) at the object itself.
Ember probably encourage to use this into the serializer, because putting this into model seems only related to a specific Rails option, and it's not straightforward at all. Moreover, putting this into the serializer fulfill this role, with or without ActiveModelSerializer.
Hope it's clear for you, if not, let me know so I can edit this.

Ember Model correct way Post and Comments Async

I have a initial page where shows all posts.
Post as many comments, but in this initial page I don't wanna show comments. When User click in a post, the application goes to url posts/post/1 in this page I wanna show the comments of post.
My server has this endpoint
My server should provide separates endpoints for /posts and /comments or posts/:id/comments/ ?
what/how is the right way to fetch(fetch or find)?
Model
// Post
export default DS.Model.extend({
message: DS.attr('string'),
comments: DS.hasMany('comment'),
});
// Comment
export default DS.Model.extend({
message: DS.attr('string'),
post: DS.belongsTo('post'),
});
The best solution would be for your server to return the comments along with the post - called "sideloading".
So, if you called a GET on /post/1 and that post had comments 1,2, and 3, the JSON returned would include the "post" as well as those three comments
When you navigate to that URL "posts/post/1" you can get the dynamic segment value i.e 1 in your case.
In your model hook (in post route) find comment with that dynamic segment value
Eg:
model: function() {
return this.store.find("comments", post_id);
}
In your controller you can access this model by
var temp = this.get('model');

How to use Ember Data with Nested Resources

My application backend has several resources. A model is exposed for each resource.
The entry point to all other resources is through the User model. What I mean is, given a User we can find BlogPost. Given a BlogPost we can find Comments etc.
In Ember terminology, we could say:
User hasMany BlogPost
BlogPost hasMany Comment
Comment belongsTo BlogPost
By backend exposes REST APIs of the form:
GET /api/v1/users/1
GET /api/v1/users/1/blog_posts/1
GET /api/v1/users/1/blog_posts/1/comments/1
I'm trying to figure out how to use Ember Data to fetch Comment belonging to a certain BlogPost belonging to a certain User.
From what I see, if I define a typical Ember model for Comment:
App.Comment = DS.Model.extend({
...
blogPost: DS.belongsTo('App.BlogPost')
});
and in the CommentRoute I have the following:
var CommentRoute = MessageRoute.extend({
model: function(params) {
this.store.find('comment')
},
The request is sent to:
/api/v1/comments
I don't even know where to begin in order for Ember Data to use urls of the form:
GET /api/v1/users/1/blog_posts/1/comments/1
I've seen several similar questions asked (see links below), but haven't seen a definitive answer to any of them. Most of them are almost a year old when ember-data, perhaps, did not have such functionality (or so it is claimed in some of these threads).
I ask again to confirm whether ember-data has such functionality or not.
Similar Questions:
Ember Data nested Models
Canonical way to load nested resources
Deep nested routes
The best way to handle it is with links. If you don't want to do it like that, it is far from supported, and difficult to hack in (the pipeline just doesn't easily pass the information around). Personally I'd recommend rolling your own adapter in that case (Ember without Ember Data).
App.Foo = DS.Model.extend({
name: DS.attr('string'),
bars : DS.hasMany('bar', {async:true})
});
App.Bar = DS.Model.extend({
foo: DS.belongsTo('foo'),
});
json:
{
id: 1,
name: "bill",
links: {
bars: '/foo/1/bars'
}
}
Example: http://emberjs.jsbin.com/OxIDiVU/971/edit

Creating Child Records in hasMany Relationship with Ember.Js

I haven't found a satisfactory answer through my search, so I figured I'd ask here.
I'm currently using Ember.Js, Ember-Data, and Ember-Firebase-Adapter, and attempting to create a CRUD application which will create a Parent Record, and then subsequent Child Records to said Parent Records.
(note that DS.Firebase.LiveModel is the Firebase adapter equivalent of DS.Model/Ember.Model)
Here are my models, altered to be generic Post/Comment types
App.Post = DS.Firebase.LiveModel.extend({
name: DS.attr('string'),
body: DS.attr('string'),
date: DS.attr('date'),
comments: DS.hasMany('App.Comment', {embedded: 'always'})
});
App.Comment = DS.Firebase.LiveModel.extend({
message: DS.attr('string'),
timestamp: DS.attr('string'),
post: DS.belongsTo('App.Post', {key: "post_id"})
});
(Should my post_id = post?)
And here is my route for creating Comments:
App.PostsCommentRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', App.Comment.find());
}
});
Here's my controller for the PostsCommentRoute:
App.PostsCommentController = Ember.ObjectController.extend({
newMessage: null,
newTimestamp: null,
saveComment: function() {
App.Pbp.createRecord({
message: this.get('newMessage'),
timestamp: this.get('newTimestamp')
})
App.store.commit();
this.set('newMessage', null);
this.set('newTimestamp', null);
}
});
I think I may be missing the serializer? And I've read several things on addArray but the things I tried to plug in did not prove fruitful. Because my comments create fine, however they are not associated to the post in anyway in my JSON.
Is there a way for the created Comments to find the related Post_Id and then associate to said Post when created? So my Post JSON has an array of Comment_Ids which then allows them to be displayed with the post?
Any help, or links with good examples would be much appreciated. I know this is a relatively simple quandary yet I've been stuck on it for some time now :p
What you can try and do is this
post = App.Post.find(1);
post.get('comments').pushObject(App.Comment.createRecord({})); //This will add a new comment to your post and set the post_id as the post id
App.store.commit()
Hope it helps

Partial update in Ember-Data?

Imagine a bug tracker.
Each ticket contains a lot of data. Now the status of a few tickets from a long list of tickets is updated.
App.Ticket = DS.Model.extend({
id: DS.attr('number'),
status: DS.attr('string'),
...
});
Currently when invoking save in ember-data this would send the complete models to the server.
What can I do to only send a partial update, like [{"id": 1, "status": "closed"}, {...}]?
PS: I understand that this is not possible right now, so I'm wondering if there is a workaround / modification that would enable this? (a general-purpose solution is not required).
My current workaround is to specify a readOnly option that excludes the attributes that I don't want to be sent in the POST.
Details can be found here: https://github.com/emberjs/data/pull/303#issuecomment-13993905
I was looking into this as well and if you want this to apply to all saves, then today you would fix it with a serializer like serializers/ticket.js:
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
attrs: {
status: {serialize: false}
}
});
This would strip out status.