Amilcar Erazo
Amilcar Erazo

Reputation: 51

How fetch model from nested resource backend in emberjs

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

Answers (4)

Vinoth Kumar
Vinoth Kumar

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

Mio
Mio

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

filby
filby

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

artych
artych

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

Related Questions