Search code examples
ember.jsember-dataember-cli

Display belongsTo category for a product


I render a product table at http://localhost:4200/products which should include the category of each product but doesn't.

Screenshot of the application

The template code:

app/templates/products/index.hbs

[...]
<tbody>
  {{#each product in model}}
    <tr>
      <td>
        {{product.name}}
      </td>
      <td>
        {{product.category.name}}
      </td>
[...]

Why doesn't {{product.category.name}} display the product.name? How can I fix it? Are the mocks wrong?

The Application

ember new shop
cd shop
ember install:addon ember-cli-scaffold
ember g scaffold product name:string
ember g scaffold category name:string
ember g adapter application
ember g http-mock products
ember g http-mock categories

app/adapters/application.js

import DS from 'ember-data';

export default DS.RESTAdapter.extend({
  'namespace': 'api'
});

app/models/product.js

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  category: DS.belongsTo('category', { async: true })
});

app/models/category.js

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  products: DS.hasMany('product')
});

server/mocks/products.js

module.exports = function(app) {
  var express = require('express');
  var productsRouter = express.Router();

  productsRouter.get('/', function(req, res) {
    res.send({
      'products': [
          {"id":1,"name":"Orange","category":1},
          {"id":2,"name":"Banana","category":1},
          {"id":3,"name":"Potato","category":2}
        ]
      });
  });
  [...]

server/mocks/categories.js

module.exports = function(app) {
  var express = require('express');
  var categoriesRouter = express.Router();

  categoriesRouter.get('/', function(req, res) {
    res.send({
      'categories': [
        {"id":1,"name":"Fruits","products:":[1,2]},
        {"id":2,"name":"Vegetables","products:":[3]}
        ]
      });
  });
  [...]

Solution

  • You should define your belongsTo to be async:

    import DS from 'ember-data';
    
    export default DS.Model.extend({
      name: DS.attr('string'),
      category: DS.belongsTo('category', { async: true })
    });
    

    this will do a GET request for every category you access.

    Or you can sideload them with products.

    Have a look at this jsbin it shows it can work.

    If you look at your mock server you'll see this route:

    categoriesRouter.get('/:id', function(req, res) {
      res.send({
        'categories': {
          id: req.params.id
        }
      });
    });
    

    As you can see when you request category 1 it'll return a object with just that id.

    So adding { async: true } does a request per category to /category/1 (when it's category 1 ofcourse). You have to make sure you return the right object.

    For example:

    var categoryList = [
      {"id":1,"name":"Fruits","products:":[1,2]},
      {"id":2,"name":"Vegetables","products:":[3]}
    ]
    
    categoriesRouter.get('/:id', function(req, res) {
      res.send({
        'categories': categoryList.filter(function(c) { 
          return c.id == req.params.id; 
        })[0]
      });
    });