$30 off During Our Annual Pro Sale. View Details »

Rainy Day Ember Data

Rainy Day Ember Data

Demos are great at teaching us the basics of working with a framework. However, what are you "todo" when your app is faced with an API that doesn't play along? In this talk, I'll be discussing the Ember Data workarounds I've discovered while building a non-trivial Ember application.

Tony Schneider

March 30, 2014
Tweet

More Decks by Tony Schneider

Other Decks in Programming

Transcript

  1. Rainy Day Ember Data
    Tony Schneider (@tonywok)!
    Neo Innovation

    View Slide

  2. Managing client side state is hard.

    View Slide

  3. Your API is a snowflake.

    View Slide

  4. Flexibility
    You need it

    View Slide

  5. …just drop down to $.ajax()

    View Slide

  6. View Slide

  7. View Slide

  8. Adapter

    View Slide

  9. – RTFM
    “An adapter is an object that receives requests
    from a store and translates them into the
    appropriate action to take against your
    persistence layer.”

    View Slide

  10. build the url

    View Slide

  11. 1 App.ApplicationAdapter = DS.ActiveModelAdapter.extend({!
    2 !
    3 host: 'https://jedimingle.com',!
    4 !
    5 namespace: '/api',!
    6 !
    7 headers: function() {!
    8 return {!
    9 "AUTHORIZATION": "Bearer" + this.get("blueprints.accessToken")!
    10 };!
    11 }.property("blueprints.accessToken")!
    12 !
    13 });

    View Slide

  12. View Slide

  13. 1 App.JediKnightAdapter = App.ApplicationAdapter.extend({!
    2 !
    3 pathForType: function() {!
    4 var dasherized = Ember.String.dasherize(type);!
    5 return Ember.String.pluralize(dasherized);!
    6 }!
    7 !
    8 });!
    9 !
    10 // We can now do the following!
    11 //!
    12 jedi.save(); // POST /jedi-knights

    View Slide

  14. Serializer

    View Slide

  15. In Ember Data a Serializer is used to serialize
    records when they are transferred out of the
    store.

    View Slide

  16. View Slide

  17. Serialization Flow
    customize json payload
    customize model’s json key

    View Slide

  18. 1 App.Cantina = DS.Model.extend({ ... });!
    2 !
    3 App.CantinaSerializer = DS.RESTSerializer.extend({!
    4 !
    5 serializeIntoHash: function(hash, type, record, options) {!
    6 hash.store = this.serialize(record, options);!
    7 },!
    8 !
    9 serialize: function(record, options) {!
    10 var json = this._super(record, options);!
    11 !
    12 json.weaponPolicy = json.lightSaberPolicy;!
    13 delete json.lightSaberPolicy;!
    14 !
    15 return json;!
    16 }!
    17 !
    18 });
    Notice no return

    View Slide

  19. Normalizer

    View Slide

  20. In Ember Data a Normalizer is used to
    transform API payloads into objects suitable for
    the store.

    View Slide

  21. View Slide

  22. Normalization Flow
    Called in all cases.!
    Good for removing extra data !
    from payload.
    Store’s meta data for specific !
    type.
    Good for top level!
    restructuring into ember!
    data types.
    General purpose, across all keys
    Normalizing specific to!
    part of the payload

    View Slide

  23. Restructure and Normalize
    1 // {!
    2 // jedis: [{!
    3 // id: 10, name: "Luke Skywalker", mitochlorian_count: 937,!
    4 // lightsabers: [!
    5 // { lightsaber_id: 5, color: "blue" },!
    6 // { lightsaber_id: 6, color: "green" }!
    7 // ]!
    8 // }]!
    9 // }

    View Slide

  24. 1 App.JediSerializer = DS.ActiveModelSerializer.extend({!
    2 !
    3 extractSingle: function(store, primaryType, payload, id, requestType) {!
    4 var lightsabers = payload.lightsabers;!
    5 var jedi = payload.jedis[0];!
    6 !
    7 jedi.lightsaber_ids = lightsabers.map(function(saber) {!
    8 return saber.lightsaber_id;!
    9 });!
    10 !
    11 delete(payload.lightsabers);!
    12 delete(payload.jedis);!
    13 !
    14 payload = { jedi: jedi, lightsabers: lightsabers };!
    15 return this._super(store, primaryType, payload, id, requestType);!
    16 },!
    17 !
    18 normalizeHash: {!
    19 lightsabers: function(hash) {!
    20 hash.id = hash.lightsaber_id;!
    21 delete(hash.lightsaber_id);!
    22 return hash;!
    23 }!
    24 }!
    25 });

    View Slide

  25. A Small Confession
    So I lied a little bit… there’s no “Normalizer”.
    The Serializer does both normalization (into) and
    serialization (out of) the store.
    But it helped with my mental model.
    DS.Serializer

    View Slide

  26. Transforms

    View Slide

  27. – RTFM
    “The DS.Transform class is used to serialize
    and deserialize model attributes when they are
    saved or loaded from an adapter.”

    View Slide

  28. Complex Properties
    // GET /jedis/1!
    // {!
    // jedi: {!
    // id: 1,!
    // name: "Luke Skywalker",!
    // lightSabers: [{color: "blue", dps: 100}, {color: "green", dps: 250}]!
    // }!
    // }!
    Ruh roh

    View Slide

  29. 1 Ember.Application.initializer({!
    2 name: 'customTransforms',!
    3 initialize: function(container, application) {!
    4 application.register('transform:objectArray', App.ObjectArrayTransform);!
    5 }!
    6 });!
    7 !
    8 App.ObjectArrayTransform = DS.Transform.extend({!
    9 !
    10 serialize: function(objectArray) {!
    11 return JSON.stringify(objectArray.get('content'));!
    12 },!
    13 !
    14 deserialize: function(json) {!
    15 if (!Ember.isArray(json)) throw "invalid object array";!
    16 if (Ember.isNone(json)) json = [];!
    17 var sizes = json.map(function(obj) { return Em.Object.create(obj); });!
    18 return Em.ArrayProxy.create({ content: sizes });!
    19 }!
    20 });!
    21

    View Slide

  30. App.Jedi = DS.Model.extend({!
    !
    name: DS.attr("string"),!
    lightSabers: DS.attr("objectArray"),!
    !
    totalDps: function() {!
    var lightSabers = this.get('lightSabers');!
    if (!lightSabers) return 0;!
    return lightSabers.reduce(function(sum, size) {!
    return sum + size.get("dps");!
    }, 0);!
    }.property(“[email protected]")!
    !
    });!
    It’s an ember data object, bind away!

    View Slide

  31. View Slide

  32. Thanks.
    github.com/tonywok
    twitter.com/tonywok

    View Slide