Solution

In the last episode we had an irregular payload served to Ember and the task was to create a custom serializer that could normalize the payload so that Ember Data could understand it.

We will start with our Ember app were we left off in episode 8.4. Basically just an Ember app with a show model.

Let's create an actor model with a name property that belongsTo a show.

ember g model actor name:string show:belongsTo

Then we need to add the relationship to our show model:

vim app/models/show.js
import DS from 'ember-data';

export default DS.Model.extend({
  title: DS.attr('string'),
  description: DS.attr('string'),
  actors: DS.hasMany('actor')
});

We can also add the actors to page. Change app/templates/index.hbs to look like this:

<ul>
  {{#each model as |show|}}
    <li>
      <strong>{{show.title}}</strong> - {{show.description}}
      <ul>
        {{#each show.actors as |actor|}}
          <li>
            {{actor.name}}
          </li>
        {{/each}}
      </ul>
    </li>
  {{/each}}
</ul>

Now, we want to change our serializer back to the JSONSerializer and we want to get rid of the 'great-shows' payload key.

app/serializers/application.js

import DS from 'ember-data';

export default DS.JSONSerializer.extend({
  normalizeResponse(store, primaryModelClass, payload, id, requestType){
    if(payload.hasOwnProperty('great-shows') && requestType === 'findAll'){
      let newResponse = this._super(store, primaryModelClass, payload['great-shows'], id, requestType);
      return newResponse;
    } else {
      return this._super(...arguments);
    }
  }
});

This setup will remove the unneeded 'great-shows' key, but we still need to handle the relationships. We need to get the embedded records into the Ember Data store.

import DS from 'ember-data';

export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
  attrs: {
    actors: { embedded: 'always' },
  },
  normalizeResponse(store, primaryModelClass, payload, id, requestType){
    if(payload.hasOwnProperty('great-shows') && requestType === 'findAll'){
      let newResponse = this._super(store, primaryModelClass, payload['great-shows'], id, requestType);
      return newResponse;
    } else {
      return this._super(...arguments);
    }
  }
});

So, this should work. But it results in one somewhat annoying behavior. In our console we get this warning:

WARNING: There is no attribute or relationship with the name `actors` on `actor`. Check your serializers attrs hash.

This is just Ember Data telling us that the actor model doesn't have an attribute actors. "Ok", we might say, "So what? I didn't say it did." But, we are using the ApplicationSerializer which means this serializer gets called for all model types and since we said actors are always embedded, Ember Data assumes we are a liar. To silence this warning, remove the DS.EmbeddedRecordsMixin from the ApplicationSerializer, and we can create a new show serializer at app/serializers/shows.js. This new serializer should extend from the ApplicationSerializer rather than the DS.JSONSerializer.

import DS from 'ember-data';
import ApplicationSerializer from './application';

export default ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, {
  attrs: {
    actors: { embedded: 'always' },
  }
});

That does it. We have successfully serialized an irregular payload. This week we will be looking at creating and updating records and we will deploy to Heroku.

Links

Brandon Blaylock

Brandon started coding over 15 years ago. After taking a short break to teach college, he found his way back to software development. He spends most of his time on the front-end writing JavaScript, but enjoys working on the server as well. Brandon believes that writing for DailyDrip is a great way to combine two things he cares about a great deal: software and education.

  1. Comments for Solution for last week and prep for this week

You must login to comment

You May Also Like