Ember Data v1.0.0-beta.9 Released

– By Stanley Stuart

Since Ember Data v1.0.0-beta.8, a lot has changed. Since we didn't write a blog post for beta.8, this blog post will contain features in both beta.8 and beta.9.

New Release Schedule

Since the first beta release for Ember Data 1.0, Ember Data has typically been released when we felt like critical features or bugfixes were merged. Due to the ever changing nature of Ember Data, we'd like to reward Ember Data users for keeping up with changes by providing more frequent releases until a release candidate emerges. Beginning Monday, August 18th 2014, Ember Data will release a new beta version every 3 weeks. The builds will be available on the builds page, Bower, Rubygems, and soon, NPM.

Breaking Changes

Object.create shim required

Ember Data now requires an Object.create polyfill for environments without Object.create or incorrect Object.create implementations such as Internet Explorer 8. Ember.js will be shipping with an Object.create polyfill in 1.8.0. If you are using stable builds of Ember, we recommend using ES5Shim's es5-sham.js file available on NPM and Bower. You can refer to Kangax's Compatibility Tables to see if you need the shim.

Ember Data uses Object.create under the hood for faster and collision-free caches.

Dates Serialized with ISO8601 by Default

Due to the various ways dates can be serialized across the wire, Ember Data has decided to use the well-known, well-supported, and accurate ISO8601 format. Although the JavaScript programming language has had support for ISO8601 strings since ECMAScript5, environment such as Internet Explorer 8 do not support Date.prototype.toISOstring. However, Ember Data does include a shim so you need no further work here except to make sure your backend is supporting ISO8601.

RESTAdapter.prototype.findMany changed behavior and method signature

You should see the findMany documentation if you have overridden the findMany method in your adapter.

HasMany Coalescing Now Opt-In

See the section below on "Coalescing Find Requests" for more information. Previously, if you did not sideload data for a hasMany relationship, but did provide IDs, Ember Data would attempt to get all the records in one request.

For example:

// Given this payload:
{
  "author": {
    "id": "1",
    "name": "Lon Ingram",
    "post_ids": ["1", "2", "3"]
  }
}

this.store.getById('author', '1').get('posts');

// $.ajax GET /posts?ids[]=1&ids[]=2&ids[]=3

Unless you opt in, Ember Data will instead fire 3 requests:

this.store.getById('author', '1').get('posts');

// $.ajax GET /posts/1
// $.ajax GET /posts/2
// $.ajax GET /posts/3

See the section below on "Coalescing Find Requests" for more information.

New Features and Improvements

Embedded Records Mixin

Deserializing Relationships

Thanks to Igor Terzic, Brendan Mcloughlin, and Bill Heaton, the DS.EmbeddedRecordsMixin was extracted out of DS.ActiveModelSerializer in Ember Data v1.0.0-beta.8 so that users of JSONSerializer, RESTSerializer, and ActiveModelSerializer could easily serialize and deserialize relationships. To use the code in your app, you can include the EmbeddedRecordsMixin into your serializer:

App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin);

This means that your PostSerializer will now correctly bring in data for relationships if they are embedded in the response, rather than sideloaded.

For example, here is the previous JSON data response Ember Data expected for the RESTAdapter:

// GET /posts/1
{
  "post": {
    "id": "1",
    "name": "The Mother We Share Lyrics",
    "authorId": "1"
  },
  "authors": [
    {
      "id": "1",
      "name": "CHVRCHES"
    }
  ]
}

this.store.find('post', '1').then(function(post){
  console.log(post.get('author.name')); // => CHVRCHES
});

Now, if you mixin the EmbeddedRecordsMixin, Ember Data will understand the following payload:

// GET /posts/1
{
  "post": {
    "id": "1",
    "name": "The Mother We Share Lyrics",
    "author": {
      "id": "1",
      "name": "CHVRCHES"
    }
  }
}

this.store.find('post', '1').then(function(post) {
  console.log(post.get('author.name')); // => CHVRCHES
});

These settings are configurable. See the section below on "Serializing Relationships."

Serializing Relationships

The EmbeddedRecordsMixin also has support for sending information about relationships to the server. To override the defaults, you can configure the EmbeddedRecordsMixin by defining an attrs object on your serializer definition. For example, to serialize the complete record when serializing to the server:

App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
  attrs: {
    author: {
      serialize: 'records'
    }
  }
});

var post = this.store.getById('post', '1');
post.set('name', 'Recover Lyrics');
post.save();

// POST /posts/1
{
  "post": {
    "name": "Recover Lyrics",
    "author": {
      "id": "1",
      "name": "CHVRCHES"
    }
  }
}

To see even more ways to customize serializing and deserializing behavior, check out the documentation for the EmbeddedRecordsMixin.

Coalescing Find Requests

One feature we're particularly excited about is the ability to coalesce find requests for the same record type.

To introduce this feature, I'll explain the basic concept behind Ember.js's "Run Loop". Typically in JavaScript code that re-renders whenever the data changes, changing the data multiple times within the same turn of the JavaScript event loop would cause multiple re-renders. Ember uses a microlibrary called Backburner to reduce the number of writes by only rendering once per "run loop". For more information, you can view the README on the Backburner on the repository and view the Ember.js Run Loop Guide.

Without "Coalescing Find Requests" turned on, the previous code would result in multiple network requests:

this.store.find('post', '1');
this.store.find('post', '2');
this.store.find('post', '3');

// $.ajax GET /posts/1
// $.ajax GET /posts/2
// $.ajax GET /posts/3

On the server-side, this is frequently referred to as an N+1 query.

By coalescing (also known as batching) these requests, Ember Data will observe that you requested several records of the same type and only send one request instead of 3.

this.store.find('post', '1');
this.store.find('post', '2');
this.store.find('post', '3');

// $.ajax GET /posts?ids[]=1&ids[]=2&ids[]=3

Coalescing find requests is currently turned off by default. To turn it on, you can use the following code:

DS.RESTAdapter.reopen({
  coalesceFindRequests: true
});

To override how the records are requested, you may override the findMany and findHasMany methods on your adapter.

Ember Inflector is Now a Standalone Package

Ember Inflector is Ember's approach for a Rails-compatible API for inflecting strings. This provides methods such as Ember.String.pluralize and Ember.String.singularize, and hooks for defining your own inflections. Previously, the Ember Inflector package was contained in Ember Data's repository and released alongside Ember Data. It has been pulled out to a separate repository. Although Ember Inflector is still included in the Ember Data release, you may now use it in projects that do not use Ember Data. Ember Inflector is currently released as a 1.0 semver package.

Ember Inflector String Caching

Ember Inflector now caches lookups of strings by default. This means that you should only pay the cost of transforming a string (via pluralize and singularize) once as the values are now stored using an in-memory cache. If you have memory concerns, you may want to monitor lookups and disable the cache by calling Ember.Inflector.inflector.disableCache() at the beginning of your app code.

Improved Uncountable / Irregular Definitions for Singularize

We would like to express our deep appreciation to Olivia Briggs for adding better support for uncountable/irregular singular words in Ember-Inflector. You should now not need definitions for dasherized and underscore versions of your inflections.

Handlebars Helpers for Inflections

You can now use pluralize and singularize in your Handlebars templates:

{{pluralize "octopus"}}
{{singularize "oxen"}}

The Handlebars helpers are bound, so they will stay up to date if you bind to a property:

{{pluralize type}}

Performance Improvements

Thanks to Stefan Penner from the Ember.js core team, your apps should be faster when used with Ember Data. We'd also like you to try out Ember 1.8 beta in your apps with Ember Data 1.0.0-beta.9 for additional performance improvements.

We have more performance improvements around the corner! Keep an eye out for Stef landing some commits on improving pushPayload calls and a commit to Backburner improving many hot code paths in Ember Data.

Better Support for Nested Records.

buildURL now takes a record, on which you can look up the relationship if you need to build a nested URL. For example:

App.CommentAdapter = DS.RestAdapter.extend({
  buildURL: function(type, id, record) {
    return '/posts/' + record.get('post.id') + '/comments/' + id;
  }
})

Added support for preloading records

For more information, go to Store documentation.

Special Thanks

We'd like to thank the following members of the Ember.js community for their continued contributions to Ember Data:

  • Igor Terzic as the project's main contributor (@terzicigor)
  • Brendan McLoughlin (@BezoMaxo) for responding to and triaging issues, and contributing documentation, bug fixes, and improvements.
  • Ilya Radchenko (@knownasilya)
  • Bradley Priest (@bradleypriest)
  • Bill Heaton (@pixelhandler)
  • Paul Chavard (@tchak)
  • Sylvain Mina (GH @sly7-7)
  • Ryunosuke Sato (@tricknotes)
  • Alexandre de Oliveira (@kurko) for his awesome work on the ember-localstorage-adapter and ember-indexeddb-adapter.
  • Jonathan Collins (Github @jcollins1991)
  • Stefan Penner (@stefanpenner)
  • Tom Dale (@tomdale)
  • Yehuda Katz (@wycats)

We’d also like to thank Instructure and PrecisionNutrition for sponsoring some of Igor’s development on Ember Data.

Tools