---
title: Getting Started with AngularJS and Rails 4
published: "2013-12-11"
publisher: Honeybadger
author: Starr Horne
category: Ruby articles
tags:
  - Ruby
  - Rails
  - JavaScript
description: This article will teach you how to plug AngularJS into Rails 4.
url: "https://www.honeybadger.io/blog/beginners-guide-to-angular-js-rails/"
---

Getting started with AngularJS isn't hard. [The documentation](http://docs.angularjs.org/api) is some of the best out there and it's tutorials are simple enough.

**But things get tricky when you start combining technologies.**

If you're using CoffeeScript instead of straight JavaScript, you know have preprocessing concerns to take into account - as well as the obvious syntax difference. These are minor issues by themselves, but what if you throw Ruby on Rails, Jasmine and Karma into the mix? It gets surprisingly trickier.

This is exactly the stack we're going to use in this tutorial. Not because we're gluttons for punishment, but _because this is the kind of setup you'll see in the real world._

This tutorial assumes that you're comfortable with Rails, but not necessarily AngularJS.

## Creating a base Rails app

Since there are so many technology layers involved, I'm going to build a simple application that barely does anything. We'll be setting up CRUD functionality for restaurants - actually, just the CR part. The -UD is left as an exercise for the reader. ;-)

We'll call the application **Restauranteur**.

I'm using PostgreSQL and RSpec here, but the DBMS and server-side testing framework are not significant. You can use whatever you want.

## Initial setup

First create the project:

```
$ rails new restauranteur --database=postgresql --skip-test-unit
```

If you're using [Pow](http://pow.cx/), add your project to Pow:

```
$ ln -s /Users/jasonswett/projects/restauranteur ~/.pow/restauranteur
```

Create the PostgreSQL database user:

```
$ createuser -P -s -e restauranteur
```

Add RSpec to your Gemfile:

```
# Gemfile gem "rspec-rails", "~> 2.14.0"
```

Install RSpec:

```
$ bundle install $ rails g rspec:install
```

Create the database:

```
$ rake db:create
```

## Creating the Restaurant model

Now that we have our project and database created, let's create our first resource. The Restaurant resource will have only one attribute: **name**, which is a string.

```
$ rails generate scaffold restaurant name:string
```

Now, just to be OCD about it, we'll make sure restaurant names are unique.

```ruby
# db/migrate/[timestamp]_create_restaurants.rb class CreateRestaurants < ActiveRecord::Migration def change create_table :restaurants do |t| t.string :name t.timestamps end # Add the following line add_index :restaurants, :name, unique: true end end
```

Run the migration:

```
$ rake db:migrate
```

Let's add some specs to verify that we can't create invalid restaurants. Notice that unique failure gives raw error.

```ruby
require 'spec_helper' describe Restaurant do before do @restaurant = Restaurant.new(name: "Momofuku") end subject { @restaurant } it { should respond_to(:name) } it { should be_valid } describe "when name is not present" do before { @restaurant.name = " " } it { should_not be_valid } end describe "when name is already taken" do before do restaurant_with_same_name = @restaurant.dup restaurant_with_same_name.name = @restaurant.name.upcase restaurant_with_same_name.save end it { should_not be_valid } end end
```

Adding these validators will make the specs pass:

```ruby
class Restaurant < ActiveRecord::Base validates :name, presence: true, uniqueness: { case_sensitive: false } end
```

We're now good to move on.

## Bringing AngularJS into the mix

Rather than dump everything on you at once, I'd first like to demonstrate the simplest "Hello, world" version of an AngularJS-Rails application and then build our restaurant CRUD functionality onto that.

There's no reason our "Hello, world" page must or should be tied to any particular Rails resource. For this reason, we'll create a `StaticPagesController` to serve up our AngularJS home page.

### Create the controller

```
$ rails generate controller static_pages index
```

Our root route right now is just the "Welcome to Rails" page. Let's set it to the `index`action of our new `StaticPagesController`:

```ruby
# config/routes.rb Restauranteur::Application.routes.draw do # Add the following line root 'static_pages#index' end
```

### Download Angular

1. In order to get our tests to work properly later, we'll need a file called`angular-mocks.js`. I don't think there's any mention of this in the Angular docs anywhere, but it's necessary.
2. In the [AngularJS tutorial](http://docs.angularjs.org/tutorial), the docs list the latest bleeding-edge version, but if I recall correctly, I had problems with compatibility between`angular.js` and `angular-mocks.js` for the latest version. I know that versions 1.1.5 worked together, so even though that's not the latest stable version, that's the version I'm listing here. Of course as time goes on the compatibility situation will probably improve.

Download `angular.js` and `angular-mocks.js` from code.angularjs.org and move the files into `app/assets/javascripts`.

```
$ wget http://code.angularjs.org/1.1.5/angular.js \ http://code.angularjs.org/1.1.5/angular-mocks.js $ mv angular* app/assets/javascripts
```

### Add it to the asset pipeline

Now we want to tell our application to require the AngularJS file, and we want to make sure it gets loaded before other files that depend on it. (We could use something like [RequireJS](http://requirejs.org/) to manage these dependencies, and that's probably what I would do on a production product, but for the purposes of this tutorial I want to keep the technology stack as thin as possible.)

_Note:_ Angular and Turbolinks can conflict with one another, so we disable them here

```javascript
// app/assets/javascripts/application.js // This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. // // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details // about supported directives. // //= require jquery //= require jquery_ujs // Add the following two lines //= require angular //= require main //= require_tree .
```

### Set up the layout

We'll add ng-app and ng-view, which signal that we have an Angular app in our page. Also notice that mentions of Turbolinks have been removed.

```
<%= yield %>
```

### Creating an Angular controller

First let's create a directory for our controllers. You can name it whatever you want.

```
$ mkdir -p app/assets/javascripts/angular/controllers
```

Now let's create the controller file itself. I'm calling this controller the "home controller," and the convention in Angular is to append your controller filenames with`Ctrl`. Thus our filename will be`app/assets/javascripts/angular/controllers/HomeCtrl.js.coffee`:

```coffeescript
# app/assets/javascripts/angular/controllers/HomeCtrl.js.coffee @restauranteur.controller 'HomeCtrl', ['$scope', ($scope) -> # Notice how this controller body is empty ]
```

### Add an Angular route

Now we'll add a routing directive in order to make our `HomeCtrl` be our "default page." Here I'm defining my routing in `app/assets/javascripts/main.js.coffee`, but again I don't think the filename matters.

```coffeescript
# app/assets/javascripts/main.js.coffee # This line is related to our Angular app, not to our # HomeCtrl specifically. This is basically how we tell # Angular about the existence of our application. @restauranteur = angular.module('restauranteur', []) # This routing directive tells Angular about the default # route for our application. The term "otherwise" here # might seem somewhat awkward, but it will make more # sense as we add more routes to our application. @restauranteur.config(['$routeProvider', ($routeProvider) -> $routeProvider. otherwise({ templateUrl: '../templates/home.html', controller: 'HomeCtrl' }) ])
```

### Add an Angular template

We'll also want a place to keep our Angular templates. I decided to put mine in`public/templates`. Again, you can place them wherever you like.

```
mkdir public/templates
```

If we create a file `public/templates/home.html` with some arbitrary content, we should be able to see it in the browser.

```
This is the home page.
```

Now, if you go to `http://restauranteur.dev/` (or `http://localhost:3000/` if you're not using Pow) and you should see the contents of `home.html`.

### An example of data binding

That's _kind of_ interesting, but probably not very impressive. Let's actually send something across the wire. Edit your`app/assets/angular/controllers/HomeCtrl.js.coffee` like this:

```coffeescript
# app/assets/angular/controllers/HomeCtrl.js.coffee @restauranteur.controller 'HomeCtrl', ['$scope', ($scope) -> $scope.foo = 'bar' ]
```

This is kind of analagous to saying `@foo = "bar"` in a Rails controller. We can plug`foo` into the template by using the double-brace syntax like this:

```
Value of "foo": {{foo}}
```

## Doing it for real this time

We've already built a simple hello world app. Creating a full blown CRUD application isn't much harder.

### Seed the database

Working with our restaurant CRUD will be a little more meaningful if we start with some restaurants in the database. Here's a seed file you can use.

```
# db/seeds.rb Restaurant.create([ { name: "The French Laundry" }, { name: "Chez Panisse" }, { name: "Bouchon" }, { name: "Noma" }, { name: "Taco Bell" }, ])
```

```
rake db:seed
```

### Creating a restaurant index page

First let's create a template folder for restaurants:

```
mkdir public/templates/restaurants
```

The first template we'll create is the index page:

```
[index](/#) * {{ restaurant.name }}
```

I'll explain in a moment what these things mean. First let's create the controller:

```coffeescript
# app/assets/javascripts/angular/controllers/RestaurantIndexCtrl.js.coffee @restauranteur.controller 'RestaurantIndexCtrl', ['$scope', '$location', '$http', ($scope, $location, $http) -> $scope.restaurants = [] $http.get('./restaurants.json').success((data) -> $scope.restaurants = data ) ]
```

Lastly, we'll adjust our routing configuration:

```coffeescript
# app/assets/javascripts/main.js.coffee @restauranteur = angular.module('restauranteur', []) @restauranteur.config(['$routeProvider', ($routeProvider) -> $routeProvider. when('/restaurants', { templateUrl: '../templates/restaurants/index.html', controller: 'RestaurantIndexCtrl' }). otherwise({ templateUrl: '../templates/home.html', controller: 'HomeCtrl' }) ])
```

Now, finally, we can go to the URI `/#/restaurants` and we should be able to see our list of restaurants. Before we move on let's add a test.

## Adding our first test

Add JS test folder:

```
mkdir spec/javascripts
```

Write test:

```coffeescript
# spec/javascripts/controllers_spec.js.coffee describe "Restauranteur controllers", -> beforeEach module("restauranteur") describe "RestaurantIndexCtrl", -> it "should set restaurants to an empty array", inject(($controller) -> scope = {} ctrl = $controller("RestaurantIndexCtrl", $scope: scope ) expect(scope.restaurants.length).toBe 0 )
```

Add config:

```javascript
// spec/javascripts/restauranteur.conf.js module.exports = function(config) { config.set({ basePath: '../..', frameworks: ['jasmine'], autoWatch: true, preprocessors: { '**/*.coffee': 'coffee' }, files: [ 'app/assets/javascripts/angular.js', 'app/assets/javascripts/angular-mocks.js', 'app/assets/javascripts/main.js.coffee', 'app/assets/javascripts/angular/controllers/RestaurantIndexCtrl.js.coffee', 'app/assets/javascripts/angular/*', 'spec/javascripts/*_spec.js.coffee' ] }); };
```

Install Karma and start the server:

```
sudo npm install -g karma sudo npm install -g karma-ng-scenario karma start spec/javascripts/restauranteur.conf.js
```

If you go to `http://localhost:9876/`, our test will run and be successful. If you'd like to see the test fail, change `expect(scope.restaurants.length).toBe 0` to`expect(scope.restaurants.length).toBe 1` and run the test again.

The meaningfulness of this test we just added is obviously questionable, but my intention here is to save you the work of figuring out how to get your Angular code into a test harness. There are certain things, like the CoffeeScript preprocessor and`angular-mocks.js` inclusion that are totally not obvious and took me several hours of head-scratching to get right.

## Building out the restaurants page

Let's now make a temporary adjustment to our restaurant index template:

```
* {{restaurant.name}} ({{restaurant.id}})
```

If you now revisit `/#/restaurants`, you'll notice that none of the restaurants have their IDs. Why are they blank?

When you generate scaffolding in Rails 4, it gives you some `.jbuilder` files:

```
$ ls -1 app/views/restaurants/*.jbuilder app/views/restaurants/index.json.jbuilder app/views/restaurants/show.json.jbuilder
```

If you open up `app/views/restaurants/index.json.jbuilder`, you'll see this:

```
# app/views/restaurants/index.json.jbuilder json.array!(@restaurants) do |restaurant| json.extract! restaurant, :name json.url restaurant_url(restaurant, format: :json) end
```

As you can see, it's including `:name` but not `:id`. Let's add it:

```
# app/views/restaurants/index.json.jbuilder json.array!(@restaurants) do |restaurant| json.extract! restaurant, :id, :name json.url restaurant_url(restaurant, format: :json) end
```

If you save the file and refresh `/#/restaurants`, you should see the IDs appear.

Now let's change the template back to the way it originally was:

```
[index](/#) * {{ restaurant.name }}
```

You may have noticed at some point that we're pointing these things at something called `viewRestaurant()` but we never actually defined anything called`viewRestaurant()`. Let's do that now:

```coffeescript
# app/assets/javascripts/angular/controllers/RestaurantIndexCtrl.js.coffee @restauranteur.controller 'RestaurantIndexCtrl', ['$scope', '$location', '$http', ($scope, $location, $http) -> $scope.restaurants = [] $http.get('./restaurants.json').success((data) -> $scope.restaurants = data ) # Add the following lines $scope.viewRestaurant = (id) -> $location.url "/restaurants/#{id}" ]
```

The convention in Rails is that `resource_name/:id` maps to a "show" page, and that's what we'll do here. Let's create a show template, route and controller.

```
# {{restaurant.name}}
```

```coffeescript
# app/assets/javascripts/main.js.coffee @restauranteur = angular.module('restauranteur', []) @restauranteur.config(['$routeProvider', ($routeProvider) -> $routeProvider. when('/restaurants', { templateUrl: '../templates/restaurants/index.html', controller: 'RestaurantIndexCtrl' }). when('/restaurants/:id', { templateUrl: '../templates/restaurants/show.html', controller: 'RestaurantShowCtrl' }). otherwise({ templateUrl: '../templates/home.html', controller: 'HomeCtrl' }) ])
```

```coffeescript
# app/assets/javascripts/angular/controllers/RestaurantShowCtrl.js.coffee @restauranteur.controller 'RestaurantShowCtrl', ['$scope', '$http', '$routeParams', ($scope, $http, $routeParams) -> $http.get("./restaurants/#{$routeParams.id}.json").success((data) -> $scope.restaurant = data ) ]
```

Now if you refresh `/#/restaurants` and click on a restaurant, you should find yourself at that restaurant's show page. Yay!

## That's all for now

We may not have seen particularly impressive results, but I hope I've saved you some time plugging AngularJS into Rails 4. Next I might recommend looking into[ngResource](http://docs.angularjs.org/api/ngResource.$resource), which can help make CRUD functionality more DRY.

## Interested in learning more?

Check out the great post by Adam Anderson, whose [Bootstrapping an AngularJS app in Rails 4.0](http://asanderson.org/posts/2013/06/03/bootstrapping-angular-rails-part-1.html) series helped me get started with AngularJS and Rails. You might like to go through his tutorial as well, but this tutorial is different in the sense that I try to \_really\_spoon-feed you all the details, minimizing the chances you'll get stuck in the weeds.

---

## Try Honeybadger for FREE

Intelligent logging, error tracking, and Just Enough APM™ in one dev-friendly platform. Find and fix problems before users notice.

[Start free trial](https://app.honeybadger.io/users/sign_up)

[See plans and pricing](https://www.honeybadger.io/plans/)
