1

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

3
  • Could you post some code that you've tried? There are a few ways to go about this. Commented Jun 12, 2015 at 2:27
  • 1
    As far as I know there is no straight Ember way to deal with nested resources api. There is long discussion about nested resources api support: github.com/emberjs/data/issues/186, github.com/emberjs/rfcs/pull/4. If you manage the backend, you might find it is simpler to change backend api to avoid using nested resources. Commented Jun 12, 2015 at 12:08
  • By nested do you mean embedded? Commented Sep 9, 2015 at 7:10

4 Answers 4

3

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 });
    }
  }

});
Sign up to request clarification or add additional context in comments.

5 Comments

is it still something that we should do ?
If I were you I'd submit PR to update "ember-data-url-templates" for ember-data 2.0. I am sure there are some words to change only.
I would like but I just started ember last week. Seems a little bit early. :)
Agree :) I'll try to look at this on weekend.
FWIW: I've been using ember-data-url-templates for ~ 6 months in production. Let me know if you end up trying it and run in to any issues :)
2
+50

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;
   }
});

Comments

1

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.

Comments

0

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";
    }
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.