Javascript Ember Ruby Setting up an Ember App with a Rails Backend

Update: June 26, 2014

This tutorial is now out of date, and you should instead look at Vic Ramon's Ember Tutorial for a tutorial introducing Ember with Rails.

Update: November 8th, 2013

Some of the instructions below are out of date, particularly the code in the store.js file. Please look at the Ember Data Rails Example that I have posted to Github for correct, updated code. This app can be viewed live on Heroku.

Today I'm going to show you how to setup an Ember app with a Rails backend. The process is relatively straightforward, but . there are some gotchas that I'd like to help you avoid.

The app will use Haml, CoffeeScript, Emblem, and Ember Data. In this tutorial I'll be starting a sample app called Launch Bay. Launch Bay would essentially be an ultra-lightweight version of Pivotal Tracker. In this first post I'm just going to get my app setup to receive data.

Basic App Setup

Let's hit the ground running with a new app

rails new launchbay -d postgresql

Setup your database and rvm gemset, and make sure to include these in your Gemfile:

gem 'coffee-rails' , '~> 4.0.0' gem 'ember-rails' gem 'ember-source' gem 'emblem-rails' gem 'haml-rails'

Open your configs and add the following:

# environments/production.rb config . ember . variant = :production

# environments/development.rb config . ember . variant = :development

# environments/test.rb config . ember . variant = :development

Backend Setup

Ok, now we've got the basics we need to get our Rails backend up and running. Our app is just going to show a list of stories. For the sake of simplicity these won't be scoped to a particular user or project.

Let's create a stories table.

rails g migration create_stories name :string body :text rake db :migrate

We'll also need a story model, controller, and serializer.

# app/models/story.rb class Story < ActiveRecord :: Base end

# app/serializers/story_serializer.rb class StorySerializer < ActiveModel :: Serializer attributes :name , :body end

I'm going to create a versioned api for the controller just in case I want to change things later:

# config/routes.rb namespace :api do namespace :v1 do resources :stories , only: :index end end

This controller is going to accept json so that we can interact with our Ember app.

# app/controllers/api/v1/stories_controller.rb class Api :: V1 :: StoriesController < ApplicationController respond_to :json def index respond_with Story . all end private def story_params params . require ( :story ). permit ( :name , :body ) end end

To see if this is all setup properly spin up your server and open a rails console. First let's seed the database:

$ rails console $ Story.create ( name: 'User views a list of stories' , body: 'Given I am a user <br /> When I visit the stories index <br /> Then I should see a list of stories' )

Now hit the api controller and you should get your one story back as json: http://localhost:3000/api/v1/stories.json

Providing an Outlet for Ember

We need to have a place for our Ember app to actually show up, so let's do that now. I am going to create a generic home controller for now.

# config/routes.rb root to: 'home#index'

# app/controllers/home_controller.rb class HomeController < ApplicationController end

Now give Ember an outlet in the view:

# app/views/home/index.html.haml %script { type: 'text/x-handlebars' } {{ outlet }}

We also need a Rails layout:

# app/views/application.html.haml !!! % html ( lang = "en-US" ) % head % title Launch Bay = stylesheet_link_tag "application" = javascript_include_tag "application" = csrf_meta_tags % body = yield

Setting up Ember Internals

First, run the generator provided by ember-rails:

rails g ember:bootstrap -g --javascript-engine coffee -n App

Now restart your Rails server and hit localhost:3000 . You should see a blank page. Open your developer console and you should see output like this:

DEBUG: ------------------------------- DEBUG: Ember.VERSION : 1.0.0 DEBUG: Handlebars.VERSION : 1.0.0 DEBUG: jQuery.VERSION : 1.10.2 DEBUG: -------------------------------

For more advanced debugging, check out the Ember Inspector for Google Chrome

Setting Up Ember Data

We'll be using Ember Data to handle client-server communication.

The Ember-rails generator created a store.js file. Open it and remove everything, and just add the following:

# app / javascripts / store . js DS . RESTAdapter . reopen namespace : 'api/v1'

Ember Models

Now to create the actual story model:

# app/assets/javascripts/models/story.js.coffee App . Story = DS . Model . extend name : DS . attr ( 'string' ) body : DS . attr ( 'string' )

Ember Routes

app/assets/javascripts/router.js contains our top-level routes.

Open that file, change it to Coffeescript, then add this:

# app/assets/javascripts/router.js.coffee App . Router . map () -> @ resource 'stories'

Note that resource is singular, in contrast to Rails.

Ember Template

Create the following file:

# app/assets/javascripts/templates/stories.js.emblem | Hello world.

Save that, now head over to http://localhost:3000/#/stories . You should see your message on the page.

There a few things to note.

Use a pipe to tell emblem that "Hello" is text and not an html tag. We created a file named stories in the templates folder. This works right now because the stories route has no subroutes. If you added a subroute to the stories resource, then this template would need to be moved to templates/stories/index.emblem.js. We didn't need to create a stories controller or stories route to make this work. Ember will create those in memory for us if we don't explicitly create them.

Pulling in real data

Ok, we've got text showing up on the page, but we need to actually get data out of our database.To do that we need to bind data to the route, and we do that by creating a stories route.

# app/assets/javascripts/routes/stories.js.coffee App . StoriesRoute = Ember . Route . extend model : -> @ get ( 'store' ). findAll ( 'story' )

Open the template back up and add the following:

# app/assets/javascripts/templates/stories.js.emblem h1 Story Listing = each story in controller h2= story.name | {{{story.body}}}

Open up http://localhost:3000/#/stories and you should see the story you put in the database.

I'm doing {{{story.body}}} to prevent the html line breaks from being escaped.

Notice that I am referring to the array of stories in the template as controller . That's because the data is bound do the Stories controller (which is still in memory).

Wrap Up

That's it for app setup with Rails, Ember, and Ember Data. If you were to continue with this project the next step would be to get project models setup, then nest stories under projects in the Ember router. I hope this was helpful.