Picture of Michael Lee
:wave: Hey hey, I'm Michael Lee and this is my site about being a developer, being a dad and making side projects.

Looping through many-to-many Relationships in Ember.js

Written on May 29, 2014

Lately I’ve been learning how to program with Ember.js – a MVC Javascript framework. In trying to focus on learning Ember.js alone, I’ve been relying on FIXTURES for my data. FIXTURES are a great way to populate some data to be used with your Ember.js application without having to hook up to an API server.

Recently I started a project that dealt with setting up models for many-to-many relationships. Based on Ember.js’ documentation, I set up my models and FIXTURES like this:

App.Driver = DS.Model.extend({
  name: DS.attr('string'),
  cars: DS.hasMany('car')
});

App.Driver.FIXTURES = [
  {
    id: 1,
    name: "Johnny",
    cars: [1, 3]
  },
  {
    id: 2,
    name: "Sally",
    cars: [1, 2]
  },
  {
    id: 3,
    name: "Carl",
    cars: [2, 3]
  },
  {
    id: 4,
    name: "Kerrigan",
    cars: [1]
  }
];

App.Car = DS.Model.extend({
  name: DS.attr('string'),
  drivers: DS.hasMany('driver')
});

App.Car.FIXTURES = [
  {
    id: 1,
    name: "Station Wagon",
    drivers: [1, 2, 4]
  },
  {
    id: 2,
    name: "Pickup Truck",
    drivers: [2, 3]
  },
  {
    id: 3,
    name: "Electric Coupe",
    drivers: [1, 3]
  }
];

<script type="text/x-handlebars" data-template-name="driver">
  <ul>
  {{#each}}
    <li>
      <div class="driver">{{name}}</div>
      <div class="cars">
        {{#each car in cars}}
          {{car.name}}
        {{/each}}
      </div>
    </li>
  {{/each}}
  </ul>
</script>

You would think this lists out each driver and each of the cars associated to a driver. But it doesn’t.

Turns out that when using FIXTURES you need to pass a { async:true } flag to the hasMany definition to make it an async relationship. According to a thread on Ember.js’ forums:

…when async is true, it will fetch the related entities when you actually request them…

This makes sense because prior to passing the async flag on the hasMany definition, I looked at my console and the Data tab revealed that the car model had 0 records. Meaning, although I had called a loop to list all the cars attribtued to a driver, the cars weren’t being fetched.

So in order to make the inner-loop that’s supposed to list the cars for a driver work, we pass the { async:true } flag to hasMany in our driver model.

App.Driver = DS.Model.extend({
  name: DS.attr('string'),
  cars: DS.hasMany('car', { async:true })
});

This now renders a list of drivers with each car that they drive listed below them.

Thanks for taking the time to read this article. I'd love to stay in touch and share more tips on programming and design and side projects. Sign up and I'll send you my articles straight to your email.