Posts tagged with "emberjs"

Warming up to Ember.js

Posted by Dan Gebhardt on Jan 20, 2012

As client-side frameworks go, Ember.js already has quite a history. It originated within the Sproutcore project as a lean and modern successor to Sproutcore 1.x, the heavyweight and component-laden framework that Apple used to build MobileMe and then iCloud. When it became apparent that Sproutcore 2 would never be a complete replacement for Sproutcore 1.x, it was renamed Amber.js as a clean break for the two projects. Shortly thereafter it was renamed again to Ember.js to avoid confusion with the Amber Smalltalk project.

I’m just glad this round of musical names didn’t end on SproutcoreCore.js.

We’re not here for a history lesson…

Ok, ok. So, what’s so great about Ember? In a word: synchronization.

Objects can observe and update each other. Computed properties can be bound to one or more other properties. Element attributes in views can be bound to objects’ properties. Views can manage the creation and removal of other views. Events can be bound to actions contained in views. Et cetera.

In other words, Ember keeps the components of your application in sync. You can focus on defining your application’s objects and views and the relationships between them, rather than the plumbing required to maintain those relationships.

Does this lead to slow performance?

There’s no doubt that Ember’s synchronization code has a performance cost. But then again, so does any code you’ve strung together to watch events and then update data and HTML. The difference is that Ember allows you to write less code and provides a nice MVC architecture in which to write it.

It’s important to remember that any client-side framework is simply constructing javascript and HTML, and that abstractions should be used judiciously. That’s why I really appreciate Ember’s default use of Handlebars templates, which allow you to mix in as much straight HTML as you like. Ember’s magic can be used only where needed.

Sounds good, but is it Production ReadyTM?

Yehuda Katz of the Ember core team wrote that they have worked with “several large companies, including ZenDesk, BazaarVoice and LivingSocial”. Ember is also in use by the wonderful open source Travis CI project (and the code is on Github).

At Cerebris we are integrating Ember into the next generation of Syncd.

But make no mistake that Ember, not quite at its 1.0 release, is under steady development and certain parts are in flux. The documentation is coming along, but does not touch upon many useful parts of the framework. I’ve found it helpful to review the code and issues on Github as well as questions on StackOverflow.

What about features X, Y and Z?

We initially had reservations about using Ember because it lacked several features included in other Javascript MVC frameworks, like a RESTful persistence layer and client-side routing. However, there are now solutions for these and other problems being developed both within and outside the official Ember project.

I’ll address these issues and more in an upcoming series of posts on Ember. While I’m cooking up my next post, please take some time to read the Ember.js home page to familiarize yourself with features such as bindings, computed properties and auto-updating templates. I’ll pull these pieces together into a simple single page Rails-backed CRUD app and share the code on Github.


1/21 update: removed general use of the word “bindings”, which has a specific meaning in Ember.

1/24: My next post is now available… Beginning Ember.js on Rails: Part 1.

Beginning Ember.js on Rails: Part 1

Posted by Dan Gebhardt on Jan 24, 2012

This is part of a series of posts on Ember.js. Please read Warming up to Ember.js as well as the Ember.js home page if you’re new to Ember.js.

Ember.js is not dependent upon any particular server stack. In fact, it can be used on its own to control single page web or mobile PhoneGap apps that don’t have a backend at all. Check out the canonical Ember Todos app for a simple online example.

I’ve chosen to write a demonstration app that I wish I could have referenced when I started learning Ember. It’s a simple Rails 3.1 app that demonstrates CRUD much like the standard Rails scaffolding. It has a front-end written entirely in Ember that communicates with its backing resources through their REST interfaces.

Just lovely, isn’t it?

ember_rest_example

DIY Demo

Let’s first get the example app running on your own system. This example will be used throughout this series, so you’ll want to refer to it often.

Just pull it down from Github and fire it up in Ruby 1.9.2+:

Terminal
git clone https://github.com/dgeb/ember_rest_example
cd ember_rest_example
bundle install
bundle exec rake db:migrate
rails s

Point your browser to localhost:3000 to give this CRUD-dy app a try.

Dependencies

This example has just a few dependencies beyond the basic Rails 3.1 stack.

The straightforward ember-rails gem pre-compiles handlebars templates in the asset pipeline. It also simplifies including ember.js, but my preference is to build or download a particular version for stricter control.

For persistence, this example uses Ember REST, a very simple library that I created (and open sourced) for managing RESTful resources. I tried to keep this library as lean and understandable as possible. Other alternatives for persistence include Ember Data, which is in heavy development by the Ember team and “definitely alpha-quality” (in their words), and Ember Resource.

Architecture

Ember does not require that your files be organized in a particular way. However, I find it helpful to organize Ember applications with the following structure within app/assets/javascripts:

  • app/ - the container for all js and templates related to this Ember app
  • app/app.js - declaration of a global App object that extends Ember.Application
  • app/models/ - model classes that represent resources
  • app/controllers/ - controllers that manage resources and are bound to views
  • app/views/ - view classes, organized in subfolders
  • app/templates/ - handlebars templates, organized in subfolders to match views
  • app/helpers/ - handlebars helpers
  • vendor/ - vendored libraries like ember.js and ember-rest.js
  • lib/ - general customizations to Ember

Note that I’m including vendor and lib in the app/assets/javascripts directory. This allows for a clear distinction between vendor/ember.js and ember.js, which would come from the ember-rails gem. However, feel free to follow the Rails convention of using vendor/assets/javascripts and lib/assets/javascripts instead.

The ember_rest_example app follows this pattern:

ember_rest_project

Adding Ember to your own app

Let’s now go through the steps of adding Ember to any new or existing Rails 3.1+ app. Start by modifying your Gemfile to include:

Gemfile
gem 'ember-rails'

From your app’s root:

Terminal
bundle

Next, duplicate the directory structure described above. From your app’s root:

Terminal
cd app/assets/javascripts
mkdir app app/controllers app/helpers app/models app/templates app/views lib vendor

Pull down the latest stable or development version of ember.js and add it to your app/assets/javascripts/vendor directory. I recommend this approach to ensure complete control over your js.

Because the Asset Pipeline doesn’t include dependency management concepts, everything must be included in the proper order within the js file you include on your pages. For instance, our example’s application.js contains:

app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require ./vendor/ember
//= require ./vendor/ember-rest
//= require_tree ./lib
//= require app/app
//= require_tree ./app/models
//= require_tree ./app/controllers
//= require_tree ./app/views
//= require_tree ./app/helpers
//= require_tree ./app/templates

You can use this as a starting point, but remove the ember-rest line if you’re using a different persistence library.

You’re now ready to create a global Ember App object. It’s not important that this object be named App. You might prefer to use your app’s name to personalize this object and namespace your whole app. Either way, creating your Ember app can be as simple as:

app/assets/javascripts/app/app.js
App = Ember.Application.create();

Hopefully that was easy. Your Rails app is now configured to work with Ember!


Please continue on to Beginning Ember.js on Rails: Part 2, which demonstrates querying a resource’s REST interface to display a list of records in an Ember view.

Beginning Ember.js on Rails: Part 2

Posted by Dan Gebhardt on Jan 26, 2012

This is part of a series of posts on Ember.js. Please read Warming up to Ember.js as well as the Ember.js home page if you’re new to Ember.js.

As promised in Part 1 of this series, this post will show how the example app uses our ultra-simple Ember REST library to query a resource’s REST interface to display a list of records.

Server-side Rails

Our Rails code is close to the minimum needed to serve up a RESTful JSON interface. It was originally generated with rails g scaffold and then simplified further.

Model

The Rails model for contacts is quite basic:

app/models/contact.rb
class Contact < ActiveRecord::Base
  validates :first_name, :presence => true
  validates :last_name, :presence => true
end

Controller

The controller is a simplified version of the scaffolding:

app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
  # GET /contacts
  # GET /contacts.json
  def index
    @contacts = Contact.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @contacts }
    end
  end

  # GET /contacts/1.json
  def show
    @contact = Contact.find(params[:id])

    respond_to do |format|
      format.json { render json: @contact }
    end
  end

  # POST /contacts.json
  def create
    @contact = Contact.new(params[:contact])

    respond_to do |format|
      if @contact.save
        format.json { render json: @contact, status: :created, location: @contact }
      else
        format.json { render json: @contact.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /contacts/1.json
  def update
    @contact = Contact.find(params[:id])

    respond_to do |format|
      if @contact.update_attributes(params[:contact])
        format.json { render json: nil, status: :ok }
      else
        format.json { render json: @contact.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /contacts/1.json
  def destroy
    @contact = Contact.find(params[:id])
    @contact.destroy

    respond_to do |format|
      format.json { render json: nil, status: :ok }
    end
  end
end

There are a couple interesting aspects to this controller code. First of all, the only action that responds to an html request is index. I’ve trimmed the others to respond only with json.

Also note that the update and destroy actions respond to success with:

render json: nil, status: :ok

… instead of the scaffolded default (head :ok), which jQuery.ajax() interprets as an error.

Routes

As you can see, we’re routing root requests to our contacts controller:

config/routes.rb
EmberRestExample::Application.routes.draw do
  root :to => 'contacts#index'
  resources :contacts
end

Let’s move to the client-side before we get back to our server-side views…

Client-side Ember

We’re now going to take a quick look at the client-side Ember model, view and controller code needed to display a list of contacts. This code is all organized within app/assets/javascripts/app/, as discussed in part 1 of this series.

If you’re following along in the example code, note that I’m leaving out a lot of the code having to do with editing records for the sake of simplicity.

Application

As reviewed in the first part of this series, the application object can be quite simple:

app/assets/javascripts/app/app.js
App = Ember.Application.create();

This object will contain all of the models, controllers and views for the application.

Model

Let’s move along to the model:

app/assets/javascripts/app/models/contact.js
App.Contact  = Ember.Resource.extend({
  resourceUrl: '/contacts',

  fullName: Ember.computed(function() {
    return this.get('first_name') + ' ' + this.get('last_name');
  }).property('first_name', 'last_name')
});

This is an extension of Ember.Resource, which is defined in ember-rest.js as an extension of Ember.Object. The resourceUrl is the base url of the resource.

Also of interest in the model is the computed property fullName, which returns a concatenation of first_name and last_name. It defines these properties as dependencies with property('first_name', 'last_name'). If either of those dependencies is updated, then fullName will be updated too. Any bindings to fullName in views will also be updated.

Controller

Our Ember-based contacts controller is very simple:

app/assets/javascripts/app/controllers/contacts.js
App.contactsController = Ember.ResourceController.create({
  resourceType: App.Contact
});

Instead of defining a class like App.Contact, we’re creating App.contactsController as an object. It’s an instance of Ember.ResourceController, which is defined in ember-rest.js as a thin extension to an Ember.ArrayController. Its only configured property is resourceType, which links it to our model. The controller knows enough to use the resourceUrl from the model to query resources (although the resourceUrl can be overridden at the controller level if needed).

Views

Let’s now check out our Ember views.

ListContactsView renders the listing:

app/assets/javascripts/app/views/contacts/list.js
App.ListContactsView = Ember.View.extend({
  templateName:    'app/templates/contacts/list',
  contactsBinding: 'App.contactsController',

  refreshListing: function() {
    App.contactsController.findAll();
  }
});

While ShowContactView renders each row within the listing:

app/assets/javascripts/app/views/contacts/show.js
App.ShowContactView = Ember.View.extend({
  templateName: 'app/templates/contacts/show',
  classNames:   ['show-contact'],
  tagName:      'tr'
});

Each view is associated with a template via templateName. The rendered template is contained within an html element that is mapped to its corresponding view. By default this is a div, although views can override this with the tagName property. This element can be customized further with classNames.

Also note the contactsBinding property on App.ListContactsView, which binds the controller’s data to this view as the property contacts.

Templates

Let’s see how each of these views is templated in Handlebars. First up is our listing:

app/assets/javascripts/app/templates/contacts/list.handlebars
<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
  {{#each contacts}}
    {{view App.ShowContactView contactBinding="this"}}
  {{/each}}
  </tbody>
</table>

This template loops through contacts, which are bound to App.contactsController, using the each helper and renders each one using ShowContactView. Because ShowContactView has a tagName of tr, a new row will be inserted for each record. It will also render the show template:

app/assets/javascripts/app/templates/contacts/show.handlebars
<td>{{contact.id}}</td>
<td class="data">
  {{contact.fullName}}
</td>

Notice how the contactBinding in list.handlebars is passed through to the show view, which can reference the object as contact in its template.

All together now

Let’s pull this all together in a single server-rendered ERB view:

app/views/contacts/index.html.erb
<h1>Contacts</h1>

<script type="text/x-handlebars">
  {{ view App.ListContactsView }}
</script>

<script type="text/javascript">
  $(function() {
    App.contactsController.loadAll(<%= @contacts.to_json.html_safe %>);
  });
</script>

The first script block is written in Handlebars, as indicated by its type. It uses the Ember view helper to render App.ListContactsView.

The javascript block is a bit of an optimization. It loads contacts, fetched in our controller’s index action, into our Ember-based App.contactsController. The reason for this is simple: we can improve performance by loading contacts in the same request as the page. A simpler alternative to calling loadAll() would be to find all contacts after jQuery has loaded, like so:

<script type="text/javascript">
  $(function() {
    App.contactsController.findAll();
  });
</script>

Let’s recap what we’ve done here. We first configured a REST interface to our data in Rails. Next we configured our Ember controller and model to load data from that interface via ember-rest.js. Finally, we hooked up our Ember views and templates to our controller’s data.

Let’s also recap what we haven’t done here. Thanks to ember-rest.js, we haven’t needed to write any ajax code to query our REST interface. And thanks to Ember, we haven’t needed to write code to update our views when contacts are loaded into the controller’s array.

Ember will really begin to shine when we see that our views are kept in sync even as contacts are added, updated, and removed. Speaking of which, my next post will cover just that…


Please continue on to Beginning Ember.js on Rails: Part 3, the final part in this series.

Beginning Ember.js on Rails: Part 3

Posted by Dan Gebhardt on Jan 31, 2012

This is part of a series of posts about Ember.js. Please read Warming up to Ember.js as well as the Ember.js home page if you’re new to Ember.js.

As promised in Part 2 of this series, this post will show how records are added, updated and removed in our example app. This app uses our ultra-simple Ember REST library to communicate with a REST interface served by Rails. I’ll try to point out when parts of this example use the persistence library (ember-rest.js) instead of core Ember (ember.js).

Because our REST interface was completed in Part 2, let’s move right to the client-side code…

Creating records

We’ll start by extending our Ember model and views to enable creating records. As you’ll soon see, much of this work is applicable to editing records as well.

Extend the model

In order to serialize our model, we need to define its resourceName and resourceProperties. In this way, ember-rest.js can send back only the data required by our REST interface:

app/assets/javascripts/app/models/contact.js
App.Contact = Ember.Resource.extend({
  resourceUrl:        '/contacts',
  resourceName:       'contact',
  resourceProperties: ['first_name', 'last_name'],

  validate: function() {
    if (this.get('first_name') === undefined || this.get('first_name') === '' ||
        this.get('last_name') === undefined  || this.get('last_name') === '') {
      return 'Contacts require a first and a last name.';
    }
  },

  fullName: Ember.computed(function() {
    return this.get('first_name') + ' ' + this.get('last_name');
  }).property('first_name', 'last_name')
});

The optional validate() method will be called by ember-rest.js when saving records. Returning an error string or object will prevent saving an invalid record. And yes, those validations might look cleaner in CoffeeScript ;)

Extend the listing view

Our listing view needs to allow contacts to be added. Let’s modify its template first:

app/assets/javascripts/app/templates/contacts/list.handlebars
<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
  {{#each contacts}}
    {{view App.ShowContactView contactBinding="this"}}
  {{/each}}
  {{#if isNewVisible}}
    <tr>
      <td>*</td>
      <td>
        {{view App.NewContactView}}
      </td>
    </tr>
  {{/if}}
  </tbody>
</table>
<div class="commands">
  <a href="#" {{action "showNew"}}>New Contact</a>
</div>

Notice the “New Contact” link at the bottom. We’re using the {{action}} helper to delegate its click event (the default for action) to the showNew() handler on the listing’s view class:

app/assets/javascripts/app/views/contacts/list.js
App.ListContactsView = Ember.View.extend({
  templateName:    'app/templates/contacts/list',
  contactsBinding: 'App.contactsController',

  showNew: function() {
    this.set('isNewVisible', true);
  },

  hideNew: function() {
    this.set('isNewVisible', false);
  }
});

In turn, the view class sets its isNewVisible property to true. By using set(), Ember’s bindings will trigger a change in the above template. {{#if isNewVisible}} will now be true, so the extra row will be displayed in our listing.

Create a view for new contacts

As you can see in the listing’s template, a NewContactView will be created within the new row in our listing:

app/assets/javascripts/app/views/contacts/new.js
App.NewContactView = Ember.View.extend({
  tagName:      'form',
  templateName: 'app/templates/contacts/edit',

  init: function() {
    this._super();
    this.set("contact", App.Contact.create());
  },

  didInsertElement: function() {
    this._super();
    this.$('input:first').focus();
  },

  cancelForm: function() {
    this.get("parentView").hideNew();
  },

  submit: function(event) {
    var self = this;
    var contact = this.get("contact");

    event.preventDefault();

    contact.saveResource()
      .fail( function(e) {
        App.displayError(e);
      })
      .done(function() {
        App.contactsController.pushObject(contact);
        self.get("parentView").hideNew();
      });
  }
});

There’s a lot going on here!

First of all, this view’s tagName will make it a form instead of the default div. Next are a couple methods overridden from the standard Ember.View:

init() - This is called to set up a view, but before it gets added to the DOM. It’s a good place to create a new contact object and associate it with our view. This object will be referenced in templates associated with our view.

didInsertElement() - This is called immediately after our element gets inserted in the DOM. It’s a good place for us to set focus to the initial input element on our form.

There are also a couple custom methods:

cancelForm() - hides this form in our parent view (see hideNew() in the listing view above).

submit() - Ember will automatically set up event listeners for views when you add methods from a standard list (currently here). In this case, we’ll be listening for our form’s submit() event.

Within the submit() handler, we’ll attempt to create our contact with the saveResource() method defined in ember-rest.js. The jQuery deferred methods fail() and done() will handle failure and success. Failure could come from either a client-side validation problem in our model, a problem communicating with the server, or an error returned from our Rails controller. On success, the new contact will be pushed into our controller’s collection. There’s no need to refresh our views - Ember will just add a new entry to our listing!

Let’s check out the template for this view:

app/assets/javascripts/app/templates/contacts/edit.handlebars
{{#with contact}}
  {{view Ember.TextField valueBinding="first_name" placeholder="First name"}}
  {{view Ember.TextField valueBinding="last_name"  placeholder="Last name"}}
  <button type="submit">
    {{#if id}}Update{{else}}Create{{/if}}
  </button>
{{/with}}
<a href="#" {{action "cancelForm"}}>Cancel</a>

Perhaps you noticed that we aren’t applying any attributes from our form to our model before calling saveResource()? We can skip this messiness because we’ve bound Ember.TextField views to our model’s properties. Each view tracks focus and keyboard events to ensure that the model’s property stays in sync with its corresponding input field.

You might also notice that, to determine the submit button’s text, we’re checking the model’s id to see if this is a new record to create or an existing one to update. In this way, we can reuse this template to handle edits.

Editing and deleting records

Editing records is very similar to adding them. The major difference is that, in order to enable changes to be saved or cancelled, we’re going to edit a copy of our object instead of the object itself.

Because the changes needed to edit and delete records require changes in the same views, I’m going to explain them together.

Extending the show view

Let’s extend the template we’re using to display each record in our listing:

app/assets/javascripts/app/templates/contacts/show.handlebars
<td>{{contact.id}}</td>
<td class="data">
  {{#if isEditing}}
    {{view App.EditContactView}}
  {{else}}
    {{contact.fullName}}
  {{/if}}
  <div class="commands">
    {{#unless isEditing}}
      <a href="#" {{action "showEdit"}}>Edit</a>
      <a href="#" {{action "destroyRecord"}}>Remove</a>
    {{/unless}}
  </div>
</td>

You can see that we’re following the same pattern from our listing template to either show an EditContactView when isEditing is true, or contact.fullName when it’s not. Also, when we’re not editing this contact, we display links to edit / remove it.

Let’s also extend the corresponding view class:

app/assets/javascripts/app/views/contacts/show.js
App.ShowContactView = Ember.View.extend({
  templateName: 'app/templates/contacts/show',
  classNames:   ['show-contact'],
  tagName:      'tr',

  doubleClick: function() {
    this.showEdit();
  },

  showEdit: function() {
    this.set('isEditing', true);
  },

  hideEdit: function() {
    this.set('isEditing', false);
  },

  destroyRecord: function() {
    var contact = this.get("contact");

    contact.destroyResource()
      .fail( function(e) {
        App.displayError(e);
      })
      .done(function() {
        App.contactsController.removeObject(contact);
      });
  }
});

The showEdit() and hideEdit() handlers toggle isEditing, which controls whether EditContactView will be shown within the template (as seen above).

The destroyRecord() handler calls our model’s destroyResource() method, defined in ember-rest.js. It has fail() and done() deferreds, just like saveResource(). On success, the record is removed from the controller’s collection. As you could guess, Ember ensures that this change is reflected in the DOM immediately.

Extending the edit view

EditContactView is similar to NewContactView, with a few exceptions:

app/assets/javascripts/app/views/contacts/edit.js
App.EditContactView = Ember.View.extend({
  tagName:      'form',
  templateName: 'app/templates/contacts/edit',

  init: function() {
    this._super();

    // Create a new contact that's a duplicate of the contact in the parentView;
    // Changes made to the duplicate won't be applied to the original unless
    // everything goes well in submitForm()
    this.set("contact", this.get('parentView').get('contact').copy());
  },

  didInsertElement: function() {
    this._super();
    this.$('input:first').focus();
  },

  cancelForm: function() {
    this.get("parentView").hideEdit();
  },

  submit: function(event) {
    var self = this;
    var contact = this.get("contact");

    event.preventDefault();

    contact.saveResource()
      .fail( function(e) {
        App.displayError(e);
      })
      .done( function() {
        var parentView = self.get("parentView");
        parentView.get("contact").duplicateProperties(contact);
        parentView.hideEdit();
      });
  }
});

Within init(), we create a copy of the parent view’s contact. This allows us to make changes that can be discarded if the form is cancelled.

In submit(), the contact’s updated properties are copied back to the original after it’s been saved successfully. This is done using the duplicateProperties() helper from ember-rest.js.

Of course, if you’d prefer that edits be made directly to the original record, you could skip both of these steps.


Whew, we made it! I hope that this series piqued your interest enough to try Ember yourself. If you’ve just been reading along, try running and modifying the example app yourself.

Although this is the final part of “Beginning Ember.js on Rails”, I plan to blog more about Ember. There’s quite a bit more to this framework than I’ve covered here, so please let me know if you have suggestions for future posts. If you’d like to stay tuned, you can follow our RSS, Twitter and Github accounts in the lower right. Thanks! - Dan

Understanding Ember.Object

Posted by Dan Gebhardt on Mar 06, 2012

Almost every object in Ember.js is derived from a common object: Ember.Object. This object is used as the basis for views, controllers, models, and even the application itself.

This simple architectural decision is responsible for much of the consistency across Ember. Because every object has been derived from the same core object, they all share some core capabilities. Every Ember object can observe the properties of other objects, bind their properties to the properties of other objects, specify and update computed properties, and much more.

As you’ll see in this post, it’s easy to get started with Ember.Object, but also easy to overlook some of its capabilities. A better understanding of Ember.Object will help you understand the architecture of Ember itself and enable you to improve the architecture of your own application.

Creating objects

It’s almost as simple to create a new Ember.Object as a plain Javascript object:

var emberObject = Ember.Object.create({
  greeting: "hello"
});

var jsObject = {
  greeting: "hello"
};

The object literal that is optionally passed to Ember.Object.create() defines properties directly on the instantiated object, not unlike the vanilla object. However, upon inspection in your console, you can see there’s quite a bit more going on behind the scenes of the Ember object:

ember_object_vs_js_object

It would take a long tour of the Ember source to explore all the properties of even such a simple Ember object. Needless to say, it’s all required for Ember to enable bindings, observers, and more. Although it looks like quite a bit of overhead, the complexity of the Ember object is contained almost entirely in its prototype (Ember.Object.prototype) and not within the object instance.

Extending classes

To go beyond vanilla Ember objects, you can extend Ember.Object or any of its descendents to create your own classes:

var Person = Ember.Object.extend({
  species: "homo sapiens"
});

var Man = Person.extend({
  gender: "male"
});

var Woman = Person.extend({
  gender: "female"
});

Important: the object literal that is passed as an argument to extend() defines properties for the prototype of objects that will be instantiated by the class.

Now, let’s instantiate a person with create():

var joe = Man.create({
  name: "Joe"
});

Important: remember that the object literal passed to create() defines properties of the instantiated object (not its prototype).

And let’s inspect our new person:

console.log( joe.get("name") );               // "Joe"
console.log( joe.hasOwnProperty("name") );    // true
console.log( joe.get("gender") );             // "male"
console.log( joe.hasOwnProperty("gender") );  // false
console.log( joe.get("species") );            // "homo sapiens"
console.log( joe.hasOwnProperty("species") ); // false

console.log( Man.prototype.isPrototypeOf(joe) );          // true
console.log( Person.prototype.isPrototypeOf(joe) );       // true
console.log( Ember.Object.prototype.isPrototypeOf(joe) ); // true

As you can see, the prototype chain for joe includes Man.prototype, Person.prototype, and Ember.Object.prototype. Our person can correctly evaluate his name (defined directly on the instance), gender (defined on Man.prototype), and species (defined on Person.prototype). What a smart guy!

Initialization (and a common mistake!)

One of the most common mistakes for beginners to Ember is to think they’re passing properties to an instance instead of a prototype. For example:

var Person = Ember.Object.extend({
  chromosomes: ["x"] // CAREFUL !!!!!
});

var joe = Person.create();
joe.get("chromosomes").push("y");

var jane = Person.create();
jane.get("chromosomes").push("x");

// Joe and Jane are all mixed up?!?!?!?!
console.log( joe.get("chromosomes") );  // x, y, x
console.log( jane.get("chromosomes") ); // x, y, x

Why did this chromosomal mutation happen? The problem started when we added an array to our prototype when defining the Person class. This array was then shared with each object instantiated from Person.

How should we have handled this?

var Person = Ember.Object.extend({
  chromosomes: null,
  init: function() {
    this._super();
    this.set("chromosomes", ["x"]); // everyone gets at least one X chromosome
  }
});

var joe = Person.create();
joe.get("chromosomes").push("y");  // men also get a Y chromosome

var jane = Person.create();
jane.get("chromosomes").push("x"); // women get another X chromosome

// Hurray - everyone gets their own chromosomes!
console.log( joe.get("chromosomes") );  // x, y
console.log( jane.get("chromosomes") ); // x, x

When declaring objects or arrays in your classes, you’ll typically want to initialize them along with each instance in the init() function. In this way, each of your objects will receive its own unique instances of objects and arrays. Also remember to call this._super() from within init() so that init() will be called all the way up the prototype chain.

Of course, there’s nothing wrong with keeping objects or arrays directly in your prototypes if they are meant to remain constant across instances. In fact, one common pattern is to keep a default setting in the prototype that’s then duplicated for each instance in init(). These kinds of patterns are easy to implement once you realize how objects are created and initialized.

Want to play more with inheritance and initialization? I’ve extended this example on jsFiddle.

Reopening objects and classes

You can easily add properties to Ember classes after they’ve been defined using reopen():

var Person = Ember.Object.extend({
  firstName: null,
  lastName:  null
});

Person.reopen({
  middleName: null
});

Specifically, reopen() modifies the prototype that will be used for objects instantiated by a class.

If you want to add properties directly to a class, use reopenClass():

var Person = Ember.Object.extend({
  firstName: null,
  lastName:  null
});

Person.reopenClass({
  makeBaby: function(attributes) {
    return Person.create(attributes);
  }
});

var huey = Person.makeBaby({firstName: "Baby", lastName: "Huey"});

One useful pattern is to define class methods for finding and/or creating objects.

Mixing in concerns

While extending classes in a hierarchical fashion often works well, there is a tendency to end up with classes such as MaleLeftHandedPetOwnersOfPoodles. Trying to munge together a bunch of separate concerns in a single class can get messy. Ember’s answer to this problem is Ember.Mixin. Here it is in action:

var Pet = Em.Object.extend({
  name: null
});

var PetOwner = Ember.Mixin.create({
  pet: null,

  init: function() {
    this._super();
    this.set("pet", Pet.create({}));
  }
});

var Male = Ember.Mixin.create({
  chromosomes: null,

  init: function() {
    this._super();
    this.set("chromosomes", ["x", "y"]);
  }
});

var joe = Ember.Object.create(PetOwner, Male, {
  age:  25,
  name: "Joe",

  init: function() {
    this._super();
    this.get("pet").set("name", "Fifi");
  }
});

console.log( joe.get("name") );            // Joe
console.log( joe.get("age") );             // 25
console.log( joe.get("chromosomes") );     // x,y
console.log( joe.get("pet.name") );        // Fifi

In this example, we’re merging a number of separate concerns in a single object. Joe is both a PetOwner and a Male, and has other traits such as an age and name. He not only has a pet, but that pet even has a name.

The separate concerns are each represented by an Ember.Mixin. The joe object is created as a merger of the PetOwner and Male mixins, as well an object literal that represents instance properties. Mixins get initialized in the order in which they are passed to create(), which allows joe to set his pet’s name after PetOwner has created that pet.

Want to play more with this example? It’s also on jsFiddle.

But wait… there’s more :)

Mixins aren’t limited to objects that you create(). They can also be used with extend(), reopen(), and reopenClass.

Furthermore, there’s a suite of useful mixins built right into Ember core. Do you want your object to be enumerable? Mix in Ember.Enumerable. Want to create a view that triggers an action? Extend it with Ember.TargetActionSupport.

I think you’ll find that by separating lateral concerns in mixins, and hierarchical concerns with extend(), you can build a very flexible architecture for your Ember apps.


Any questions or suggestions for further Ember posts? Please feel free to comment below – Dan