This article was last updated on May 14, 2013 and reflects the state of Ember and Ember-Data's latest builds on that date.
Lately I've been playing with Ember.js and I have really grown to love it. I get the same "AHA!" feeling I got building my first Rails app 7 years ago. Let's see how to build a simple CRUD app using the RailsAPI as the backend. We're going to build a new app and deploy to Heroku.
Part 1 - Getting Set Up
gem install rails-api rails-api new ember-app cd ember-app
Similar to the
RailsAPI comes with a
command which under the hood is just using the normal
rails CLI code
but overriding some of the templates generated. Out of the box
RailsAPI won't generate the asset pipeline directories
as there is still some
debate if it will use
Rake-Pipeline or some
other solution. In this example we're going to use Sprockets as it will
save us a lot of time.
RailsAPI is bundled with
Sprockets as a dependency. All we need to do is add in the
Now we need to copy in the vendored asset files. You can either build yourself our run the following to copy directly from my Github project
Note that if you're a Mac user, just replace
wget (the Linux command) with
curl -O (the Unix command) on the above lines.
Let's setup the directory structure for our Ember app
And now we'll setup the load order in our
#= require modernizr #= require jquery #= require handlebars #= require ember #= require ember-data #= require bootstrap #= require_self #= require store #= require routes #= require_tree ./controllers #= require_tree ./models #= require_tree ./templates #= require_tree ./views window.App = Ember.Application.create()
@import 'bootstrap' body padding-top: 60px
That was a good amount of setup. Now we have the application structure for an Ember app in our asset pipeline. This will make things cleaner once we start coding.
Let's setup the necessary gem dependencies in our
Gemfile. Just replace the entire contents with the following:
source 'https://rubygems.org' ruby '1.9.3' gem 'rails', '3.2.13' gem 'rails-api' gem 'thin' gem 'active_model_serializers', :github => 'rails-api/active_model_serializers' group :development, :test do gem 'debugger' gem 'sqlite3' end group :production do gem 'pg' end group :assets do gem 'sass-rails', '~> 3.2' gem 'coffee-rails', '~> 3.2' gem 'compass-rails' gem 'uglifier' gem 'bootstrap-sass', '~> 22.214.171.124' gem 'handlebars_assets', '0.12.0' end group :development do gem 'quiet_assets' end
There are two gems to take note of:
- ActiveModelSerializers is a project that is written by the
Embercore team which will normalize the JSON output for models in a
- HandlebarsAssets will allow the
AssetPipelineto compile Handlebars templates which is required for Ember. There is the Ember-Rails gem which will also do this but I have found
HandlebarsAssetsto be a leaner solution.
After this, don't forget to run
bundle install from the command line to pick up the gems we just added.
Let's create a simple model and the serializer
rails-api g model User first_name:string last_name:string quote:text rails-api g serializer User
Now open up
app/serializers/user_serializer.rb and add the fields that require serialization
class UserSerializer < ActiveModel::Serializer attributes :id, :first_name, :last_name, :quote end
Again, this will instruct
Rails to turn our
ActiveRecord object into a
JSON object properly normalized for
Ember. Let's write the Controller. Create and edit
class UsersController < ApplicationController def index render json: User.all end end
Take note that we are inheriting
ApplicationController but in a
ApplicationController itself inherits from
ActionController::API instead of
This basic controller will serve up all of our users to our Ember app. We'll add more later.
Now let's add some routes to
EmberApp::Application.routes.draw do class FormatTest attr_accessor :mime_type def initialize(format) @mime_type = Mime::Type.lookup_by_extension(format) end def matches?(request) request.format == mime_type end end resources :users, :except => :edit, :constraints => FormatTest.new(:json) get '*foo', :to => 'ember#index', :constraints => FormatTest.new(:html) get '/', :to => 'ember#index', :constraints => FormatTest.new(:html) end
A few things are happening here:
- We are constraining against the format with a custom
FormatTestclass. We only want to map certain routes to
JSONrequests and certain routes to
get '*foo'...will greedily match all routes except
/so we have the following line. We want to direct all
HTMLrequests to a single
controller#action. I will go into the reason why in a bit.
So let's create that
Ember controller. This will act as the primary application serving controller that is hit when people visit the app. Create and edit
class EmberController < ActionController::Base; end
Note that we are inheriting from
ActionController::Base this time and not
ApplicationController. This is so that the controller actions can respond to non
Now we will add the view in
That is all the view that your Ember app will need. Ember will automatically attach its own default template to the
Let's add some data to
User.create(:first_name => 'William', :last_name => 'Harrison', :quote => "I'm just singin' in the rain!") User.create(:first_name => 'Abraham', :last_name => 'Lincoln', :quote => "I'd like to see a show tonight.")
Now run your migrations and seed
rake db:migrate db:seed
Ok, now our app is in a good spot to start developing an Ember app with. Let's review what we did
- Generated a new app using
- Wrote a very simple JSON API for returning all users
If you fire up your app you'll see a blank page. Not too interesting. In Part 2 we'll build the Ember app itself.