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.

5f2d06be136bf5fb34da25f62afefae7?s=128

Tony Schneider

March 30, 2014
Tweet

Transcript

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

  2. Managing client side state is hard.

  3. Your API is a snowflake.

  4. Flexibility You need it

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

  6. None
  7. None
  8. Adapter

  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.”
  10. build the url

  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 });
  12. None
  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
  14. Serializer

  15. In Ember Data a Serializer is used to serialize records

    when they are transferred out of the store.
  16. None
  17. Serialization Flow customize json payload customize model’s json key

  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
  19. Normalizer

  20. In Ember Data a Normalizer is used to transform API

    payloads into objects suitable for the store.
  21. None
  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
  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 // }
  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 });
  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
  26. Transforms

  27. – RTFM “The DS.Transform class is used to serialize and

    deserialize model attributes when they are saved or loaded from an adapter.”
  28. Complex Properties // GET /jedis/1! // {! // jedi: {!

    // id: 1,! // name: "Luke Skywalker",! // lightSabers: [{color: "blue", dps: 100}, {color: "green", dps: 250}]! // }! // }! Ruh roh
  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
  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(“lightSabers.@each.dps")! ! });! It’s an ember data object, bind away!
  31. None
  32. Thanks. github.com/tonywok twitter.com/tonywok