How to Wire Up Ruby on Rails and AngularJS as a Single-Page Application (Updated for 2016)

by Jason Swett,

Refreshed for 2016

I wrote my first Angular/Rails tutorial in 2014, then another one in 2015. My 2015 post used CoffeeScript. CoffeeScript, from my perspective, is probably not going to play a large role in the future of JavaScript. Angular 2 uses TypeScript by default. TypeScript is a superset of ES6, so if you’re using ES6 in an Angular app, that will presumably make it easier to upgrade to Angular 2/TypeScript than if you’re using CoffeeScript. So I think it makes more sense at this point in time for me to use ES6 than CoffeeScript for my tutorial.

My 2015 post has also not shockingly suffered from a little bit of software rot as the world has moved forward while it has stood still. If you follow it character-for-character today, it doesn’t work. I thought it would be nice to put out a new tutorial that actually works.

The sample app

There’s a certain sample app I plan to use throughout called Lunch Hub. The idea with Lunch Hub is that office workers can announce in the AM where they’d like to go for lunch rather than deciding as they gather around the door and waste half their lunch break. Since Lunch Hub is a real project with its own actual production code, I use a different project here called “Fake Lunch Hub.” You can see the Fake Lunch Hub repo here.

Setting up our Rails project

Instead of regular Rails we’re going to use Rails::API. I’ve tried to do Angular projects with full-blown Rails, but I end up with a bunch of unused views, which feels weird. First, if you haven’t already, install Rails::API. (Side note: I realize that Rails 5 offers an API-only version. Rails 5 is still in beta and I want you to be able to use this tutorial as a starting point for production projects, so I’m using Rails::API 4.2.x for this tutorial.)

Creating a new Rails::API project works the same as creating a regular Rails project.

Get into our project directory.

Create our PostgreSQL user.

Create the database.

Now we’ll create a resource so we have something to look at through our AngularJS app. (This might be a good time to commit this project to version control.)

Creating our first resource

Add gem 'rspec-rails' to your Gemfile (in the test group) and run:

When you generate scaffolds from now on, RSpec will want to create all kinds of spec files for you automatically, including some kinds of specs (like view specs) that in my opinion are kind of nutty and really shouldn’t be there. We can tell RSpec not to create these spec files:

(Now might be another good time to make a commit.)

In Lunch Hub, I want everybody’s lunch announcements to be visible only to other people in the office where they work, not the whole world. And there’s actually a good chance a person might want to belong not only to a group tied to his or her current workplace, but perhaps a former workplace or totally arbitrary group of friends. So I decided to create the concept of a Group in Lunch Hub. Let’s create a Group resource that, for simplicity, has only one attribute: name.

Since groups have to have names, let’s set null: false in the migration. We’ll also include a uniqueness index.

Now, if you run rails server and go to http://localhost:3000/groups, you should see empty brackets ([]). We actually want to be able to do http://localhost:3000/api/groups instead.

At the risk of being annoying, I wanted to include a realistic level of testing in the tutorial, at least on the server side.

To make this spec pass you’ll of course need to add a validation:

We also have to adjust the controller spec RSpec spit out for us because RSpec’s generators are evidently not yet fully compatible with Rails::API. The generated spec contains an example for the new action, even though we don’t have a new action. You can remove that example yourself or you can just copy and paste my whole file.

Now if you run all specs on the command line ($ rspec), they should all pass. We don’t have anything interesting to look at yet but our Rails API is now good to go.

Adding the client side

On the client side we’ll be using Yeoman, a front-end scaffolding tool. First, install Yeoman itself as well as generator-gulp-angular. (If you don’t already have npm installed, you’ll need to do that. If you’re using Mac OS with Homebrew, run brew install npm.)

We’ll keep our client-side code in a directory called client. (This is an arbitrary naming choice and you could call it anything.)

Now we’ll generate the Angular app itself. When I ran it, I made the following selections:

  • Angular version: 1.5.x
  • Modules: all
  • jQuery: 2.x
  • REST resource library: ngResource
  • Router: UI Router
  • UI framework: Bootstrap
  • Bootstrap component implementation: Angular UI
  • CSS preprocessor: Sass (Node)
  • JS preprocessor: ES6
  • HTML template engine: Jade

Start Gulp to see if it works:

Gulp should now open a new browser tab for you at http://localhost:3001/#/ where you see the “‘Allo, ‘Allo” thing. Our Angular app is now in place. It still doesn’t know how to talk to Rails, so we still have to make that part work.

Setting up a proxy

Thanks to Gerardo Gomez for helping me figure this one out.

Here are the things I changed, it no particular order:

  1. Added middleware that says “send requests to /api to http://localhost:3000
  2. Added a rails task that simply invokes the rails server command
  3. Added a serve:full-stack task that runs the regular old serve task, but first runs the rails task

You’ll have to install http-proxy-middleware before continuing:

Now we can run our cool new task. Make sure neither Rails nor Gulp is already running somewhere.

Two things should now happen:

  1. If you navigate to http://localhost:3001/api/foo, you should get a Rails page that says No route matches [GET] "/api/foo", which means
  2. Rails is running on port 3000.

Getting Rails data to show up in our client app

Now we’ll want to get some actual data to show up in the actual HTML of our Angular app. This is a pretty easy step now that we have the plumbing taken care of. First, create some seed data:

Get the data into the database:

Now let’s modify src/app/index.route.js to include a state for groups:

Then we add GroupsController, which at this point is almost nothing:

Lastly, let’s create a view at src/app/components/groups.jade:

If you now navigate to http://localhost:3001/#/groups, you should see a big h1 that says “Groups”. So far we’re not talking to Rails at all yet. That’s the very next step.

A good library for Angular/Rails resources is called, straightforwardly, angularjs-rails-resource. It can be installed thusly:

Now let’s add two things to src/app/index.module.js: the rails module and a resource called Group.

Now let’s add a line to our controller to make the HTTP request:

And some code in our template to show the group names:

If you now visit http://localhost:3001/#/groups, you should see your group names there. Congratulations! You just wrote a single-page application. It’s a trivial and useless single-page application, but you’re off to a good start. In my experience the plumbing is the hardest part.


Leave a Reply

Your email address will not be published. Required fields are marked *