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

ember-data upcoming features

ember-data upcoming features

Slides for talk at Ember.js Munich Meetup in June 2016

http://www.meetup.com/Ember-js-Munich/events/231881195/

Clemens Müller

June 28, 2016
Tweet

More Decks by Clemens Müller

Other Decks in Programming

Transcript

  1. Ember Thalassa • They are looking for Ember developers •

    https://www.bookingsync.com/en/jobs 18
  2. ember-twiddle • shoutout to Joost de Vries @joostdvrs • https://canary.ember-twiddle.com/

    19 // twiddle.json { "addons": { "liquid-fire": "latest", "ember-power-select": "1.0.0-alpha.5", "ember-cli-cornify": "latest" } }
  3. Once upon a time • Snapshots • Serializer refactor •

    async Relationships • JSON-API as first class citizen • … 21
  4. Once upon a time • Snapshots • Serializer refactor •

    async Relationships • JSON-API as first class citizen* • … 22
  5. • RFC process & feature flags • all aboard the

    ember release train • emberjs slack community • #-ember-data • #dev-ember-data 23 git diff master..@{may.2015}
  6. // instead of import DS from "ember-data"; const Store =

    DS.Store; // you can import directly via import Store from 'ember-data/store'; 25 proper ember-cli addon
  7. // instead of import DS from "ember-data"; const Store =

    DS.Store; // you can import directly via import Store from 'ember-data/store'; 26 proper ember-cli addon
  8. import Serializer from 'ember-data/serializers/json-api'; import Adapter from 'ember-data/adapters/json-api'; import Transform

    from 'ember-data/transform'; import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { hasMany, belongsTo } from 'ember-data/relationships'; 27 proper ember-cli addon
  9. !

  10. ds-transform-pass-options // app/models/blog.js import Model from "ember-data"; export default Model.extend({

    datePublished: attr("my-date"), modifiedAtWithTime: attr("my-date", { format: "YYYY-MM-DD HH:mm" }) }); 31
  11. ds-transform-pass-options // app/transforms/my-date.js import Transform from "ember-data/transform"; export default Transform.extend({

    serialize(value, options) { let { format = "YYYY-MM-DD" } = options; return moment(value).format(format); }, deserialize(value, options) { let { format = "YYYY-MM-DD" } = options; return moment(value, format).toDate(); } }); 32
  12. ds-boolean-transform-allow-null // app/models/user.js import Model from "ember-data/model"; export default Model.extend({

    hasAcceptedTerms: attr("boolean") }); // { // "user": { // "hasAcceptedTerms": false // } // } store.createRecord("user", {}).serialize(); 33
  13. ds-boolean-transform-allow-null // app/models/user.js import Model from "ember-data/model"; export default Model.extend({

    hasAcceptedTerms: attr("boolean", { allowNull: true }) }); // { // "user": { // "hasAcceptedTerms": null // } // } store.createRecord("user", {}).serialize(); 34
  14. ds-finder-include • why no query params for findRecord or findAll?

    • comment by @bmac in #4156 https://git.io/voHn8 • records in partial state • complexity for caching scenarios • include query params for GET requests • http://jsonapi.org/format/#fetching-includes 36
  15. ds-finder-include // GET /books/1?include=author store.findRecord("book", 1, { include: "author" });

    // GET /books?include=author store.findAll("book", { include: "author" }); 38
  16. ds-finder-include // GET /books/1?include=author store.findRecord("book", 1, { include: "author" });

    // GET /books?include=author store.findAll("book", { include: "author" }); 39 // GET /books/1?include=author { data: { type: "books", id: 1, relationships: { author: { data: { type: "authors", id: 1 } } } }, included: [{ type: "authors", id: 1, attributes: { name: "Tobias O. Fünke" } }] }
  17. ds-overhaul-queryRecord* • not a feature, but clarification for usage PR#4300

    • use store.queryRecord() when server returns a single record and id is not known beforehand: • GET /current_user • urlForQueryRecord() • store.query() & firstObject for other cases 40
  18. ds-overhaul-queryRecord* // app/adapters/user.js import Adapter from "./application"; export default Adapter.extend({

    urlForQueryRecord(query) { if (query.current) { return "/current_user"; } return this._super(...arguments); } }); let currentUser = store.queryRecord("user", { current: true }); 41
  19. ds-overhaul-queryRecord* // app/adapters/user.js import Adapter from "./application"; export default Adapter.extend({

    urlForQueryRecord(query) { if (query.current) { return "/current_user"; } return this._super(...arguments); } }); let currentUser = store.queryRecord("user", { current: true }); // GET /current_user { data: { type: "user", id: 1, ... } } 42
  20. ds-overhaul-queryRecord* // app/services/store.js import Store from "ember-data/store"; export default Store.extend({

    queryFirst() { let query = this.query(...arguments); return query.then(function(result) { return result.get("firstObject"); }); } }); 43
  21. ds-overhaul-queryRecord* // app/services/store.js import Store from "ember-data/store"; export default Store.extend({

    queryFirst() { let query = this.query(...arguments); return query.then(function(result) { return result.get("firstObject"); }); } }); let pope = store.queryFirst("user", { email: "[email protected]" }); 44
  22. ds-overhaul-queryRecord* // app/services/store.js import Store from "ember-data/store"; export default Store.extend({

    queryFirst() { let query = this.query(...arguments); return query.then(function(result) { return result.get("firstObject"); }); } }); let pope = store.queryFirst("user", { email: "[email protected]" }); // GET /users?email=... { data: [{ type: "user", id: 1, ... }] } 45
  23. ds-references • RFC #57 • low level API for record

    and relationships • plain JS objects • groundwork for future improvements • available since 2.5 46
  24. ds-references • check if record / relationship is loaded without

    triggering request • public API to get id / ids of relationship without triggering request • (re)load relationship • get meta of relationship 47
  25. ds-references let book = store.peekRecord("book", 1); let authorRef = book.belongsTo("author");

    let meta = authorRef.meta(); let id = authorRef.id(); // null if not yet loaded let author = authorRef.value(); authorRef.load().then(function(author) { }); authorRef.reload().then(...); 48 { data: { type: "books", id: 1, relationships: { author: { data: { type:" authors", id: 2 }, meta: { isFamous: true } } } } }
  26. ds-references let book = store.peekRecord("book", 1); let chaptersRef = book.hasMany("chapters");

    let meta = chaptersRef.meta(); let ids = chaptersRef.ids(); // null if not yet loaded let chapters = chaptersRef.value(); chaptersRef.load().then(function(chapters) { }); chaptersRef.reload().then(...); 49
  27. ds-references let bookRef = store.getReference("book", 1); let id = bookRef.id();

    // null if not yet loaded let value = bookRef.value(); bookRef.load().then(function(book) { }); bookRef.reload().then(function(book) { }); 50
  28. ds-improved-ajax • tackles problem of overwriting of private ajax() and

    ajaxOptions() • new public adapter hooks to get properties of request • uses ajax() and ajaxOptions() if present and log a deprecation • plan is to eventually use ember-ajax 52
  29. ds-improved-ajax // app/adapters/application.js import Adapter from "ember-data/adapters/rest"; export default Adapter.extend({

    methodForRequest(options) {}, urlForRequest(options) {}, dataForRequest(options) {}, headersForRequest(options) {} }); 53
  30. ds-improved-ajax // app/adapters/application.js import Adapter from "ember-data/adapters/rest"; export default Adapter.extend({

    methodForRequest(options) {}, urlForRequest(options) {}, dataForRequest(options) {}, headersForRequest(options) {} }); { requestType: "...", snapshot, snapshots, id, ids, query, store, type } 54
  31. ds-improved-ajax // app/adapters/application.js import Adapter from "ember-data/adapters/rest"; export default Adapter.extend({

    methodForRequest(options) {}, urlForRequest(options) {}, dataForRequest(options) {}, headersForRequest(options) {} }); { requestType: "query", query, store, type } 55
  32. ds-improved-ajax // app/adapters/application.js import Adapter from "ember-data/adapters/rest"; export default Adapter.extend({

    methodForRequest(options) {}, urlForRequest(options) {}, dataForRequest(options) {}, headersForRequest(options) {} }); { requestType: "updateRecord", snapshot, id, store, type } 56
  33. ds-improved-ajax // app/adapters/application.js import Adapter from "ember-data/adapters/rest"; export default Adapter.extend({

    methodForRequest({ requestType }) { if (requestType === "createRecord") { return "PUT"; } return this._super(...arguments); } }); 57
  34. ds-improved-ajax // app/adapters/application.js import Adapter from "ember-data/adapters/rest"; export default Adapter.extend({

    headersForRequest({ requestType, snapshot }) { let headers = this._super(...arguments); if (requestType === "findRecord") { headers["If-None-Match"] = snapshot.get("etag"); } return headers; } }); 58
  35. ds-save-include • basically ds-finder-include for save() • currently unsure about

    specific API • book.save({ include: "author" }) • does it include author relationship in request? • should author be included on response? • both? • discussion at PR #4354 59
  36. ds-reset-attribute • record.resetAttribute() to reset an attr to its latest

    acknowledged value • last canonical value pushed into store • the last inFlight value 60
  37. ds-reset-attribute let book = store.peekRecord("book", 1); book.get("title") === "old title");

    book.set("title", "new title"); book.resetAttribute("title"); book.get("title") === "old title"); book.set("title", "new title"); book.save(); book.set("title", "updated title"); book.resetAttribute("title"); book.get("title") === "new title"); 61
  38. ds-pushpayload-return • return pushed records for store.pushPayload() • not yet

    go’ed • possible performance implications • shut down the door on API if materialized records are returned for store.pushPayload() 62
  39. ds-pushpayload-return let pushedUsers = store.pushPayload({ users: [ { id: 1,

    email: "[email protected]" }, { id: 2, email: "[email protected]" } ] }); // [<App:User:1>, <App:User:2>] Ember.Logger.info(pushedUsers); // get materialized record let gob = pushedUsers[0]; 63
  40. ds-pushpayload-return let refs = store.pushPayload({ users: [ { id: 1,

    email: "[email protected]" }, { id: 2, email: "[email protected]" } ] }); // [<RecordReference:User:1>, <RecordReference:User:2>] Ember.Logger.info(refs); // get materialized record let gob = refs[0].value(); 64
  41. ds-pushpayload-return let normalized = store.normalizePayload({ users: [ { id: 1,

    email: "[email protected]" }, { id: 2, email: "[email protected]" } ] }); let refs = store.pushRef(normalized); // [<RecordReference:User:1>, <RecordReference:User:2>] Ember.Logger.info(refs); // get materialized record let gob = refs[0].value(); 65
  42. ds-improved-references • ds-references laid groundwork • next up are enhancements

    for missing parts • model lifecycle hooks RFC#123 • more type of references RFC#150 66
  43. ds-improved-references // app/models/books.js import Model from "ember-data/model"; let Book =

    Model.extend(); Book.reopenClass({ didReceiveData(bookRef) { let book = bookRef.value(); let meta = bookRef.meta(); if (book && meta) { book.set("meta", meta); } } }); export default Book; 67
  44. ds-improved-references // app/models/books.js import Model from "ember-data/model"; let Book =

    Model.extend(); Book.reopenClass({ didReceiveRelationship(parent, name, relRef) { if (name === "author") { let book = parent.value(); let meta = relRef.meta(); book.set("isFamousAuthor", meta.isFamous); } } }); export default Book; 68
  45. ds-improved-references // app/models/books.js import Model from "ember-data/model"; let Book =

    Model.extend(); Book.reopenClass({ didReceiveRelationship(parent, name, relRef) { if (name === "chapters") { let book = parent.value(); let ids = relRef.ids(); book.set("chapterIds", ids); } } }); export default Book; 69
  46. ds-improved-references // app/models/books.js import Model from "ember-data/model"; let Book =

    Model.extend(); Book.reopenClass({ didReceiveRecordArray(recordArrayRef) { let recordArray = recordArrayRef.value(); let meta = recordArrayRef.meta(); recordArray.set("meta", meta); } }); export default Book; 70
  47. ds-improved-references // GET /books store.query("book", {}) 71 { data: [...],

    links: { next: { href: "/books?page=2", meta: { page: 2 } }, ... }, meta: { total: 12 } }
  48. ds-improved-references // GET /books store.query("book", {}).then((books) => { let ref

    = books.ref(); ref.meta().total === 12; let nextPageRef = ref.links("next"); nextPageRef.meta().page === 2; // GET /books?page=2 nextPageRef.load().then(…); }); 72 { data: [...], links: { next: { href: "/books?page=2", meta: { page: 2 } }, ... }, meta: { total: 12 } }
  49. ds-improved-references // GET /books/1/relationships/chapters?include=bookmarks book.hasMany("chapters").load({ include: "bookmarks" }); // PATCH

    /books/1/relationships/chapters book.hasMany("chapters").save(); // DELETE /books/1/relationships/chapters book.hasMany("chapters").delete(); 73