Relational Data Problems


#1

So I have this dilema, and its probably just my lack of understanding of how to solve problems like this in Ember. I am using Ember data, with fixtures for now, and I have a complex object relationship. My question is, how do I get data from child objects?

For example, I have some activities, and these activities have relations to other objects in them, which I have set up the relationships in the Data model, but I cannot figure out how to access them since a route only returns a “model” which is essentially one data type. Here is some code which I hope will be a good illustration of my problem:

App.Activity = DS.Model.extend({
	contact: DS.belongsTo('contact'),
	user: DS.belongsTo('user'),
	type: DS.attr('number'),
	subject: DS.attr('string'),
	contact_id: DS.attr('number'),
	dueDate: DS.attr('Date'),
	assignedUser: DS.attr('number'),
	completed: DS.attr('boolean'),
	notes: DS.attr('string')
});

App.Contact = DS.Model.extend({
	activity: DS.belongsTo('activity'),
	firstname: DS.attr('string'),
	lastname: DS.attr('string'),
	email: DS.attr('string'),
	phone: DS.attr('string'),
	mobile: DS.attr('string'),
	organization_id: DS.attr('number'),
	department: DS.attr('string'),
	title: DS.attr('string'),
	address: DS.attr('string'),
	city: DS.attr('string'),
	state: DS.attr('string'),
	zip: DS.attr('string'),	
	notes: DS.attr('string')	
});

So my activities have relations into contact and organizations. How do I access those objects from my handlebars template?

<script type="text/x-handlebars" id="activities">
  <p>Activities</p>
  <table>
  <tr>
    <th>Type</th>
    <th>Due Date</th>
    <th>Subject</th>
    <th>Contact</th>
    <th>Done</th>
  </tr>
  {{#each item in model}}
  <tr>
    <td>{{type}}</td>
    <td>{{dueDate}}</td>
    <td>{{#link-to 'activity' this}} {{subject}} {{/link-to}}</td>
    <td>{{contact_id}}</td>
    <td>{{input type="checkbox" name="completed" checked=completed}}</td>
    </table>
  {{/each}}
  </script>

I want to displace the contact details here and not just the ID!

Similarly:

<script type="text/x-handlebars" id="activity">
  <p>Activity</p>
  <table>
  <tr>
    <th>Type</th>
    <th>Due Date</th>
    <th>Subject</th>
    <th>Contact</th>
    <th>Done</th>
  </tr>
  <tr>
    <td>{{type}}</td>
    <td>{{dueDate}}</td>
    <td>{{subject}}</td>
    <td>{{contact.firstname}}</td>
    <td>{{input type="checkbox" name="completed" checked=completed}}</td>
    </table>
  </script>

I want to display a great deal of contact information here but I cant seem to get at the contact object!

App.ActivityRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('activity');
  }
});

Do I return contacts here somehow? Not sure how to deal with complex object relations in handlebars templates…

:frowning:


#2

This isn’t answering your question, but your table headers should be wrapped in a <thead> and the body in a <tbody> you’ll run into issues where data binding will not work because Ember’s metamorph tags get mixed up with the start and ends since that’s not valid HTML.


#3

You should be accessing it through relationship defined on the model.

Your activity already has defined the

Just change your handlebars to use any of the contact’s properties like in the following example.

{{item.contact.name}}

Also, you do not need to define the contact_id and the contact as attributes. Ember Data will assume there is a field for the contact_id when you define the DS.belongsTo attribute.


#4

Thank you so much! Massive help!!

Ok I spoke too soon. LOL Now the contact data is still not loaded. When I view the data section in my Ember Chrome plugin, I have user (1), activity (2), contact (0).

Here is how I load the models in:

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('user');
  }
});

App.BookingRoute = Ember.Route.extend({
	model: function() {
		return this.store.find('activity');
	}
})


App.ActivityRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('activity');
  }
});

App.ContactRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('contact');
  }
});

So my question is: How do I load multiple models into a single route?

Ok after doing some research maybe I don’t want to load multiple models into a single route, can;t I query this inheritance and get the relating models to autoload? I mean why shouldn’t this work: {{item.contact.firstname}} in my code? Clearly it doesn’t and its clearly not loading. I am lost as to why this doesn’t work. Again some short snippets of what I am trying to do:

   <script type="text/x-handlebars" id="booking">
  <p>Booking</p>
  <table>
      <tr>
        <th>Type</th>
        <th>Due Date</th>
        <th>Subject</th>
        <th>Contact</th>
        <th>Done</th>
      </tr>
      {{#each item in model}}
      <tr>
        <td>{{item.type}}</td>
        <td>{{item.dueDate}}</td>
        <td>{{#link-to 'activity' item}} {{item.subject}} {{/link-to}}</td>
        <td>{{item.contact.firstname}}</td>
        <td>{{input type="checkbox" name="completed" checked=completed}}</td>
      </tr>
      {{/each}}
    </table>
    {{outlet}}
  </script>

#5

Hmm ok so i think I am on to something here…could this be a problem with my fixture data? Does my data have to have the relationships and their data mapped out completely in each fixture for this relationship mapping to work?

So to borrow from another post here, does my data need to look something like this:

    {
  "post": {
    "id": 1,
    "title": "Rails is omakase",
    "comments": ["1", "2"],
    "user" : "dhh"
  },

  "comments": [{
    "id": "1",
    "body": "Rails is unagi"
  }, {
    "id": "2",
    "body": "Omakase O_o"
  }]
}

Instead of having two separate fixtures?


#6

OK I FIGURED IT OUT!!

All of the docs refer to relations as contact_id, but that’s not the way it needs to be referenced in the fixture data. THAT IS CONFUSING! What needs to happen is to use the actual object name and then its id. I think the docs should be changed to reflect this. So what made my code work? Changing my data from this:

App.Activity.FIXTURES = [
	{
	   	id: 1,
	  	type: 1,
		subject: 'Email',
	  	contact_id: 1,
	   	dueDate: '2014-05-27T12:54:01',
		user: 1,
	  	completed: false,
	   	notes: 'He is very interested!'
	},
	{
	   	id: 2,
	  	type: 1,
		subject: 'Email',
	  	contact_id: 2,
	   	dueDate: '2014-05-27T12:54:01',
		user_id: 1,
	  	completed: false,
	   	notes: 'Wants a call back next week.'
	}
];

To this:

App.Activity.FIXTURES = [
	{
	   	id: 1,
	  	type: 1,
		subject: 'Email',
	  	contact: 1,
	   	dueDate: '2014-05-27T12:54:01',
		user: 1,
	  	completed: false,
	   	notes: 'He is very interested!'
	},
	{
	   	id: 2,
	  	type: 1,
		subject: 'Email',
	  	contact: 2,
	   	dueDate: '2014-05-27T12:54:01',
		user_id: 1,
	  	completed: false,
	   	notes: 'Wants a call back next week.'
	}
];

That was painful!


#7

You need to be careful where you’re sourcing information from. I personally don’t know where contact_id came from, but I recommend reading http://jsonapi.org/format/#document-structure-individual-resource-representations and http://emberjs.com/guides/models/connecting-to-an-http-server/#toc_json-conventions since ember-data has changed over time.

If you’re API(s) do not follow this convention, and you have little control over it, you’ll want to make use of the normalizePayload hook on the DS.RestAdapter to convert the payload into something that ember-data recognizes.

And since I’m sure you’ll encounter it, the hasMany relationship convention is contacts: [1, 2]


#8

Thanks Jason. Luckily I will be writing my APIs myself, so I will make sure they conform to the proper specification. :slight_smile: