Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ember Data Internals

tomdale
December 05, 2012

Ember Data Internals

tomdale

December 05, 2012
Tweet

More Decks by tomdale

Other Decks in Programming

Transcript

  1. Data

    View Slide

  2. • Core Concepts
    • Public API
    • Adapter/Serializer API
    • Internals

    View Slide

  3. Why Ember Data?

    View Slide

  4. Things It‘s Still
    Hard to Do in 2012
    (which is bullshit)

    View Slide

  5. Creating two records, setting
    up a relationship between
    them, then saving them.

    View Slide

  6. Easily starting with fixture
    data if your server isn‘t
    ready yet.

    View Slide

  7. Changing the JSON
    representation of your model
    without changing app code.

    View Slide

  8. Only loading models you
    don‘t have cached.

    View Slide

  9. Keeping the DOM
    up-to-date with model changes.

    View Slide

  10. Dynamically switching
    between a server and
    offline cache.

    View Slide

  11. Using awesome new browser
    features like WebSockets and
    TypedArrays.

    View Slide

  12. These problems are
    SO
    COMMON!

    View Slide

  13. Why do we all do
    it by hand?!

    View Slide

  14. Convention Over
    Configuration

    View Slide

  15. conventional
    conventional
    {
    json: "lol"
    }
    WHY IS THIS
    NOT CONVENTIONAL?!?!

    View Slide

  16. Ember Data

    View Slide

  17. Architecture
    Overview

    View Slide

  18. Store
    Adapter Serializer
    Record Record Record Record

    View Slide

  19. Store
    Adapter Serializer
    Record Record Record Record
    Server
    Semantics
    Application
    Semantics
    Things that wouldn't change if your
    backend changed.
    I.e., ignoring your server protocol,
    how would you ideally model your
    data? How do you think about your
    models?
    Things that are specific to
    your server/backend/
    persistence layer.

    View Slide

  20. Loading a Record

    View Slide

  21. Store
    Application
    App.Post.find(1)
    Record

    View Slide

  22. Store
    Application
    Person.find([1, 2, 3])
    RecordArray

    View Slide

  23. Store
    Application
    Person.find()
    RecordArray

    View Slide

  24. // State for '/posts'
    App.PostsState = Ember.State.extend({
    setupControllers: function() {
    var posts = App.Post.find();
    this.set('controller.content', posts);
    }
    });

    View Slide

  25. Store
    Application
    App.Post.find(1)
    Adapter
    Record
    loading

    View Slide

  26. Store
    Adapter
    Record
    loading

    View Slide

  27. Store
    Record
    Adapter
    Serializer
    loading

    View Slide

  28. Store
    Record
    Adapter
    Serializer
    loaded

    View Slide

  29. Store
    Application
    App.Post.find(1)
    Record
    loaded

    View Slide

  30. Ember Data in
    Practice

    View Slide

  31. Setup

    View Slide

  32. App.Store = DS.Store.extend();

    View Slide

  33. var attr = DS.attr;
    App.Post = DS.Model.extend({
    title: attr('string'),
    body: attr('string'),
    comments: DS.hasMany('App.Comment'),
    author: DS.belongsTo('App.User')
    });

    View Slide

  34. Finding Records

    View Slide

  35. var person = App.Person.find(1);

    View Slide

  36. person.get('isLoaded');
    //=> false
    person.get('firstName');
    //=> undefined

    View Slide

  37. wait a bit

    View Slide

  38. person.get('isLoaded');
    //=> true
    person.get('firstName');
    //=> "Tywin"

    View Slide

  39. Changing
    Records

    View Slide

  40. var tyrion = App.Person.find(2);
    tyrion.get('isDirty');
    //=> false
    tyrion.get('firstName');
    //=> "Tyrion"
    tyrion.set('firstName', "Yollo");
    tyrion.get('isDirty');
    //=> true

    View Slide

  41. store.commit();
    tyrion.get('isDirty');
    //=> true
    tyrion.get('isSaving');
    //=> true

    View Slide

  42. wait a bit

    View Slide

  43. tyrion.get('isDirty');
    //=> false
    tyrion.get('isSaving');
    //=> false

    View Slide

  44. Creating
    Records

    View Slide

  45. var eddard =
    App.Person.createRecord({
    firstName: "Eddard",
    lastName: "Stark"
    });
    var robb =
    App.Person.createRecord({
    firstName: "Robb",
    lastName: "Stark"
    });

    View Slide

  46. robb.set('parent', eddard);
    store.commit();

    View Slide

  47. Deleting
    Records

    View Slide

  48. tywin.deleteRecord();

    View Slide

  49. Transactions

    View Slide

  50. var transaction = store.transaction();
    transaction.add(tyrion);
    transaction.add(tywin);
    transaction.commit();

    View Slide

  51. transaction.rollback();

    View Slide

  52. Record States

    View Slide

  53. Loaded
    Updated
    Created
    Deleted
    Clean
    Dirty
    In-Flight In-Flight
    Error Error

    View Slide

  54. var person = App.Person.find(1);

    View Slide

  55. person.get('isLoaded');
    //=> false
    person.get('firstName');
    //=> undefined

    View Slide

  56. wait a bit

    View Slide

  57. person.get('isLoaded');
    //=> true
    person.get('firstName');
    //=> "Tywin"

    View Slide

  58. isLoaded
    isDirty
    isSaving
    isDeleted
    isError
    isNew
    isValid
    These are derived from the
    current state object, which
    is awesome!

    View Slide

  59. • Each record has an associated
    state manager.
    • The store does not make
    changes to records directly.
    • Instead, events are sent to the
    record.
    • The record responds based on
    its current state.

    View Slide

  60. Uncaught Error: could not
    respond to event setProperty in state
    rootState.loaded.created.inFlight.
    =
    You tried to change a record
    while it was being saved.

    View Slide

  61. // store.js
    record.loadedData();

    View Slide

  62. // model/model.js
    loadedData: function() {
    this.send('loadedData');
    }

    View Slide

  63. // model/states.js
    loading: DS.State.create({
    // TRANSITIONS
    exit: function(manager) {
    var record = get(manager, 'record');
    record.trigger('didLoad');
    },
    // EVENTS
    loadedData: function(manager) {
    didChangeData(manager);
    manager.transitionTo('loaded');
    }
    })

    View Slide

  64. Adapters

    View Slide

  65. • Are relationships saved in
    the parent or the child?
    • What payloads are sent
    to what URLs?
    • What actions map to
    what HTTP verbs?
    • What is the name of the
    primary key?
    • What are the names of
    attributes?
    • Are objects embedded or
    referred to by ID?
    Serializer
    Adapter

    View Slide

  66. Serializer
    JSONSerializer
    RESTSerializer

    View Slide

  67. Serializer
    Transform Values
    new Date() to
    "2007-04-05T14:30"
    (and vice versa)

    View Slide

  68. DS.JSONSerializer.registerTransform('boolean', {
    serialize: function(value) {
    return value ? 'true' : 'false';
    },
    deserialize: function(value) {
    return value === 'false' ? false : true;
    }
    });
    Serializer

    View Slide

  69. Serializer
    Add ID
    addId: function(data, key, id) {
    data[key] = id;
    }

    View Slide

  70. Serializer
    Add Attributes
    addAttribute: function(hash, key, value) {
    hash[key] = value;
    }

    View Slide

  71. Serializer
    Add Belongs-To Relationships
    addBelongsTo: function(hash, record, key, relationship) {
    var id = get(record, relationship.key+'.id');
    if (!Ember.none(id)) { hash[key] = id; }
    }

    View Slide

  72. Serializer
    Extract Attributes
    extractAttribute: function(type, hash, attributeName) {
    var key = this._keyForAttributeName(type, attributeName);
    return hash[key];
    }

    View Slide

  73. Serializer
    Declarative Syntax
    App.Adapter.map('Person', {
    firstName: { key: 'FIRST_NAME' }
    });

    View Slide

  74. Thank you.
    Questions?
    http://plus.tomdale.net
    http://emberjs.com
    @tomdale

    View Slide