Pro Yearly is on sale from $80 to $50! »

Overview of Orbit.js

Overview of Orbit.js

Overview of Orbit.js and how it can be used with Ember.js. Presented at the Boston Ember meetup February 21, 2018.

E01ec1de2f7783812d2235a6a9aaaeea?s=128

Dan Gebhardt

February 21, 2018
Tweet

Transcript

  1. @ d g e b B o s t o

    n E m b e r M e e t u p F e b r u a r y 2 0 1 8
  2. U S E C A S E S

  3. CLIENT-FIRST DEVELOPMENT

  4. PLUGGABLE SOURCES

  5. DATA SYNCHRONIZATION

  6. EDITING CONTEXTS

  7. UNDO / REDO

  8. OPTIMISTIC UI

  9. OFFLINE (BROWSER CACHE)

  10. ISOMORPHIC

  11. B A S I C C O N C E

    P T S
  12. DISPARATE SOURCES

  13. DISPARATE DATA

  14. COMMON INTERFACES

  15. NORMALIZED DATA

  16. EVENTED CONNECTIONS

  17. FLOW CONTROL

  18. CHANGE TRACKING

  19. IMMUTABLE DATA

  20. @ o r b i t p a c k

    a g e s
  21. { @orbit/core @orbit/data @orbit/coordinator @orbit/utils CORE PACKAGES

  22. { @orbit/store @orbit/jsonapi @orbit/local-storage @orbit/indexeddb SOURCES

  23. { @orbit/local-storage-bucket @orbit/indexeddb-bucket BUCKETS

  24. @ o r b i t / d a t

    a
  25. SCHEMA { MODELS RELATIONSHIPS KEYS

  26. { SOURCES SCHEMA TRANSFORM LOG QUEUES (REQUEST + SYNC) OPTIONAL

    INTERFACES
  27. { PULLABLE PUSHABLE QUERYABLE RESETTABLE SYNCABLE UPDATABLE OPTIONAL SOURCE INTERFACES

  28. UPDATING AN "UPDATABLE" SOURCE store.update( transform ); // returns a

    promise that resolves 
 // when complete
  29. TRANSFORMS Transforms consist of an array of operations: [{"op": "addRecord",

    "record": { type: 'planet', id: 'p1', attributes: { name: 'Jupiter' }}} {"op": "addRecord", "record": { type: 'moon', id: 'm1', attributes: { name: 'Io' }}}, {"op": "addToRelatedRecords", "record": { type: 'planet', id: 'p1' }, "relationship": "moons", "record": { type: 'moon', id: 'm1' }}]
  30. BUILDING TRANSFORMS Transform builders improve ergonomics: store.update(t => [ t.addRecord(jupiter),

    t.addRecord(io), t.addToRelatedRecords(jupiter, 'moons', io) ]);
  31. QUERYING A "QUERYABLE" SOURCE store.query( queryExpression ); // resolves to

    static results
  32. QUERY EXPRESSIONS An example query expression for finding a sorted

    collection: { op: 'findRecords', type: 'planet', sort: [{ kind: 'attribute', attribute: 'name', order: 'ascending' }] }
  33. QUERY BUILDERS Query builders improve ergonomics of building expressions: store.query(q

    => q.records('planet') .sort('name'));
  34. @ o r b i t / c o o

    rd i n a t o r
  35. COORDINATOR { SOURCES STRATEGIES

  36. SYNC'ING CHANGES // Sync all changes to the store with

    backup coordinator.addStrategy(new SyncStrategy({ source: 'store', target: 'backup', blocking: true }));
  37. OPTIMISTIC UPDATES // Push update requests to the server. coordinator.addStrategy(new

    RequestStrategy({ source: 'store', on: 'beforeUpdate', target: 'remote', action: 'push' }));
  38. PESSIMISTIC UPDATES // Push update requests to the server. coordinator.addStrategy(new

    RequestStrategy({ source: 'store', on: 'beforeUpdate', target: 'remote', action: 'push', blocking: true }));
  39. HANDLING FAILURES (1/2) coordinator.addStrategy(new RequestStrategy({ source: 'remote', on: 'pushFail', action(transform,

    e) { if (e instanceof NetworkError) { // When network errors are encountered, try again in 5s console.log('NetworkError - will try again soon'); setTimeout(() => { remote.requestQueue.retry(); }, 5000); } // Else see Part 2 }, blocking: true }));
  40. HANDLING FAILURES (2/2) // When non-network errors occur, notify the

    user and // reset state. let label = transform.options && transform.options.label; if (label) { alert(`Unable to complete "${label}"`); } else { alert(`Unable to complete operation`); } // Roll back store to position before transform if (store.transformLog.contains(transform.id)) { console.log('Rolling back - transform:', transform.id); store.rollback(transform.id, -1); } return remote.requestQueue.skip();
  41. BROWSER STORAGE // Warm the store's cache from backup BEFORE

    activating // the coordinator backup.pull(qb.records()) .then(transforms => store.sync(transforms)) .then(() => coordinator.activate());
  42. + = ember-orbit

  43. import { Model, attr, hasMany } from 'ember-orbit'; Star =

    Model.extend({ name: attr('string'), planets: hasMany('planet', {inverse: 'sun'}) }); Planet = Model.extend({ name: attr('string'), classification: attr('string'), sun: hasOne('star', {inverse: 'planets'}) }); Model ember-orbit
  44. Store cache.query cache.liveQuery query liveQuery update { } { }

    Synchronous Asynchronous ember-orbit
  45. // Given a `model` from a `store` let fork =

    store.fork(); let forkModel = this.fork.cache.find(model.type, model.id); fork .addRecord({ type: 'phoneNumber' }) .then(phoneNumber => { forkModel.get('phoneNumbers').pushObject(phoneNumber); }); // `merge` coalesces operations to a minimal set before // applying the update store.merge(fork); Store Forking ember-orbit
  46. W h a t ' s N e w ?

  47. orbitjs.com

  48. None
  49. None
  50. None
  51. None
  52. Thanks! @dgeb