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

Matthew Ray

Matthew is a tech news enthusiast. He spends most of his time reading tech focused news, watching sports and drinking coffee. You will probably find him at a coffee shop or the library.

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

You May Also Like