Reputation: 51
how I can fetch models nested in Ember.js. I means, I have a model called post
. In my endpoint I have a route to fetch all posts
and to fetch a specific post
with its id
. But, for example, if I show a specific post
, how can I get all comments
related with that post
clicking in a button?
My route in backend is /posts/:post_id/comments
.
Thanks
Upvotes: 1
Views: 871
Reputation: 1377
Extending @filby's answer further with JSON API 1.0 specification which uses the new JSONAPIAdapter
, the following code works
Single Post response from server
'data': {
'type': 'posts',
'id': 1,
'attributes': {
'title:'A new post'
},
'relationships': {
'comments': {
'links':{
'related': '/api/posts/1/comments'
}
}
}
}
The Post model
// models/post.js
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { hasMany } from 'ember-data/relationships';
export default Model.extend({
title: attr('string')
comments: hasMany('comments');
});
The Comments model
// models/comment.js
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo } from 'ember-data/relationships';
export default Model.extend({
post: belongsTo('post', {async: true});
likes: attr('number')
});
The Post's Route
// post.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.findRecord('post', params.post_id);
}
});
The Post template
{{#each model.comments as |comment|}}
{{comment.likes}}
{{/each}}
The requests for comments will be /api/posts/1/comments
which is specified in the related
attribute.
Upvotes: 1
Reputation: 1502
You can make a custom buildUrl in your rest adapter.
Mine that I'm currently using (only for /posts/:post_id/comments
)
export default DS.RESTAdapter.extend({
host: 'http://www.exemple.me:3000',
namespace: 'api/v1/,
buildURL: function (modelName, id, snapshot, requestType, query) {
var url = [];
url.push(this.urlPrefix());
url.push("posts/" + query.post);
delete query.user;
url.push(this.pathForType(modelName));
return url.join('/') + ".json";
}
});
Upvotes: 0
Reputation: 378
The best way I have found to access nested resources (if you can modify your API) is for your API to return a "links" attribute in the json for comments. For example the json returned will be like:
{
"post": {
"id": 1,
"links": {
"comments": "http://localhost:3000/posts/1/comments"
}
}
}
Now you define the relationship in your ember app and fetch the model in the route:
// models/post.js
import DS from 'ember-data';
export default DS.Model.extend({
comments: DS.hasMany('comments', {async: true});
});
// models/comment.js
import DS from 'ember-data';
export default DS.Model.extend({
post: DS.belongsTo('post', {async: true});
});
// post.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('post', params.post_id);
}
});
Now, when in your template when you call
{{#each model.comments as |comment|}}
{{comment.comment_attribute}}
{{/each}}
Ember will make a async request at /posts/:post_id/comments and fetch the comments. If you wish to show a loading icon or something while the request is taking place you can wrap it in
{{#if model.comments}}
{{#each model.comments as |comment|}}
{{comment.comment_attribute}}
{{/each}}
{{else}}
<!-- Something to do while loading -->
{{/if}}
Note :You can also see this documented in Ember Doc
This findHasMany method is called when ember sees in your template {{model.comments}}
The second solution for your problem would be to customize the adapter while in your route (I dont recommend this and havent really tested it):
//one post route
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.findRecord('post', params.post_id);
}
});
//post/comments route
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var post_id = this.modelFor('post.show').get('id');
this.store.adapterFor('comment').set('namespace', 'posts/' + post_id );
var comments = this.store.findAll('comment');
// set the namespace again to null
this.store.adapterFor('comment').set('namespace', '');
return comments;
}
});
Upvotes: 2
Reputation: 3669
You could try ember-data-url-templates addon.
It allows specify urls you need by url templates. Now ember-data-url-templates
is under early development.
// console
ember install ember-data-url-templates
ember generate ember-data-url-templates
// adapters/comment.js
// add UrlTemplates mixin to your `Adapter` and specify url templates
import DS from "ember-data";
import UrlTemplates from "ember-data-url-templates";
export default DS.RESTAdapter.extend(UrlTemplates, {
urlTemplate: '{+host}/posts/{postId}/comments{/id}',
urlSegments: {
postId(type, id, snapshot, query) {
return snapshot.belongsTo('post', { id: true });
}
}
});
Upvotes: 3