5/29/2015 - 3:48 AM

Introduction To Ember Notes

Introduction To Ember Notes


If a route doesn’t have a model it looks to its parent route for its model (by default nested routes inherit the parent’s model).

this.resource(‘album’, {path: ‘/album/:album_id’}, function() {

this.resource(’song’, {path: ‘/song/:song_id’}, function() {

Then must use namespaced route name in link-to helpers and put route files in directories reflecting the same namespace.

{{#link-to 'album.comments'}}See the album comments{{/link-to}}
{{#link-to 'song.comments'}}See the album comments{{/link-to}}
  • Resources are the nouns of your application.
  • Routes are the refinements (adjectives / verbs) i.e. edit, approve, new, favorited.
  • Use nesting in your router to create nested urls that reflect the ui that the user should see when using the app.

Ember Data

Inside app/models/user.js

import DS from 'ember-data'

var attr = DS.attr,
    hasMany = DS.hasMany;

export default DS.Model.extend({
  name: attr('string'),
  age: attr('number'),
  preferredMember: attr('boolean'),
  posts: hasMany('post')

Inside app/models/post.js

import DS from 'ember-data'

var attr = DS.attr,
    belongsTo = DS.belongsTo;

export default DS.Model.extend({
  name: attr('string'),
  body: attr('string'),
  user: belongsTo('user')

The Store object (available in routes / controllers)

  • Manages requests to server and acts as caching layer to avoid redundant requests.
  • Use 'store' inside of route model hooks in order to get access to models.
import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {'user', 1); // Ember Data makes a GET request to /users/1

Post index page where we want to see all of the posts together.

import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {'post'); // using find w/ out an id makes a GET request to /posts'post', {page: 2, per_page: 20}); // GET /posts?page=2&per_page=20

Custom Helpers

Helpers and components use hyphenated names to differentiate themselves from properties and to adhere to the current W3C specification for custom elements which require element names with hyphens.


<div class='field'>
  <label>Your Name</label>
  {{input type='text' value='name'}}

<div class='field'>
  <label>First Name</label>
  {{input type='text' value='firstName'}}

<div class='field'>
  <label>Last Name</label>
  {{input type='text' value='lastName'}}

<div class='field'>
  <label>Secret Name</label>
  <p>{{format-secret name}}</p> // using custom helper
  <p>{{reverseNames this}}</p> // using Ember Object (user model)

Custom Helper

import Ember from 'ember';

function formatSecret(value)  {
  if (!value) { return ''; }
  return value.split('').reverse().join('');

export default Ember.Handlebars.makeBoundHelper(formatSecret)

Custom Helper with Ember Object

import Ember from 'ember';

function reverse(value) {
  if (!value) { return ''; }
  return value.split('').reverse().join('');

function reverseNames(user) {
  var names = [user.get('firstName'), user.get('lastName')];
  return' ');

export default Ember.Handlebars.makeBoundHelper(reverseNames, 'firstName', 'lastName')
  • Dependent keys indicate what properties the helper depends on (i.e. 'firstName' and 'lastName').

Returning HTML from a Custom Helper


<div class='left'>
  <label>Post Body</label>
  {{textarea value=body}}

<div class='right'>
  <div class='preview'>
    {{format-markdown body}}

<div class='row'>
  <button {{action 'save' model}}>Save</button>

Custom Helper returning HTML

/* global marked */ // -> Indicates use of global variable 'marked' to make dependencies clear and JSHint happy
import Ember from 'ember';

function formatMarkdown(text) {
  if (!value) { return ''; }
  return marked(text).htmlSafe();

export default Ember.Handlebars.makeBoundHelper(formatMarkdown)

Computed Properties

Ember introduces an object model on top of JavaScript's builtin prototypal inheritance (why we have to use getters).

  • Similar to inheritance systems in other languages.
  • Let's you model your application more concisely than using JavaScript's built-in prototypal inheritance.
  • Ember.Object is the root class that all other objects inherit from.
  • Whenever we are using "extend" we are using inheritance to make a subclass of an Ember object.
  • When you extend an Ember object you can give it a list of mixins before the object literal of methods.
Album = Ember.Oject.extend({


Album.create() // to instantiate

If details about how a property is managed leak into other parts of our application it means we have poor encapsulation.

To update the DOM when a model's property changes Ember uses an abstraction called computed properties which use an observer pattern to always keep the view layer up to date.

  • Using this syntax we can tell Ember that a function on an object should behave like a property not a method.
  • To declare a function a computed property call 'property' on it and pass in the dependent keys.
  • Dependent keys can go n levels deep i.e. 'assignees.length'.
export default Ember.object.extend({
  isReadyForWork: function() {
    return this.get('assignee') && this.get('description');
  }.property('assignee', 'description')

Ember Object improves JavaScript's object model in the following ways.

  • Uniform access to properties with get().
  • Observable properties (mechanism for templating system to observe changes that happen and update the DOM accordingly).
  • Computed properties have built in caching layer and only recalculate if dependent keys change.
  • Declarative way of modeling data flow (through composability of computed properties).

@each syntax in computed properties (used to manage lists of items)


Example: 'tasks.@each.isReadyForWork' means

  • Recompute if an item is added to tasks.
  • Recompute if an item is removed from tasks.
  • Recompute if isReadyForWork on a task changes.

Computed macros express common computed properties in a concise way.


  • Main benefit is components are isolated from each other so they are not affected by the outside world (the app they reside in).
  • Create a custom html element where you describe what it looks like with a handlebars template and you describe the behavior using JavaScript.
  • Pass attributes into components the same way you set attributes on an html element.
  • Components can send actions out to parent components.
  • Use components in block form to pass content in and then use "yield" inside to render content.
  • Trigger "actions" via user input, then update UI via "data-binding".

Naming Convention = {{name-of-component}}

  • app/templates/components/name-of-component.hbs
  • app/components/name-of-component.js

Ember Run Loop

The run loop lets ember officially schedule the work your application does in different queues to get the best performance and smoothest rendering.

  • Batches multiple changes to your model together and updates the browser once.
  • To take full advantage of the ember run loop you should wrap asynchronous callbacks from non ember libraries and browser apis with run loop functions.
  • Run.bind let's us integrate a function with ember's run loop and bind the context at the same time.
run(function()  {
  // code to execute within a RunLoop


  • sync
  • actions
  • routerTransitions
  • render
  • afterRender
  • destroy

Wrapping a function in run.debounce means it will be called in a certain amount of time as long as the function is not invoked again.

run.debounce(this, myFunc, 1000);
// wait 500ms
run.debounce(this, myFunc, 1000);
// myFunc invoked after 1 second (after the second time it was invoked)


  • Services are singleton objects that hold onto global state
  • Does not have a specific defined role within the Ember ecosystem
  • Services are only instantiated once

Examples of what you might use services for

  • Ember data accessible to both routes and controllers.
  • Interfacing with a geolocation api.
  • Coordinating drag-and-drop events.
  • Consuming push events from a server.
  • Using a non-crud server api.

Services can be injected into other ember objects using the injected property syntax.

  • The name passed into the service fn() isn't required if your property name matches the service name.
export default Ember.Service.extend({
  serviceName: Ember.inject.service('service-name');

  setup: function() {
    this.get('serviceName') // If no part of the object 'gets' the service then it will never be created



  • A unit test for a component is an integration test on only its part of the DOM that does not touch the outside app.

Unit Tests

  • Exercise a small unit of your application while trying to isolate it from its dependencies.
  • Call functions or methods on an object then either assert against the resulting return value or assert that some side effect like updating the DOM occurred.

Integration Tests

  • Simulate user interaction and exercise many parts of your application together.

End to End Tests (acceptance tests / feature tests)

  • Exercise your entire application stack including your server.
  • Usually involve driving a web browser through a realistic test setup of your application.