Grunt Single-Page Applications

How to Wire Up Ruby on Rails and AngularJS as a Single-Page Application

This post is now somewhat outdated. If you’re interested in Angular/Rails/Grunt/JavaScript/HTML, read on. If you’d prefer Angular/Rails/Gulp/CoffeeScript/Jade, check out this newer post.

Why this tutorial exists

I wrote this tutorial because I had a pretty tough time getting Rails and Angular to talk to each other as an SPA. The best resource I could find out there was Ari Lerner’s Riding Rails with AngularJS. I did find that book very helpful and I thought it was really well-done, but it seems to be a little bit out-of-date by now and I couldn’t just plug in its code and have everything work. I had to do a lot of extra Googling and head-scratching to get all the way there. This tutorial is meant to be a supplement to Ari’s book, not a replacement for it. I definitely recommend buying the book because it really is very helpful.

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. I also created a branch specifically to match up with this tutorial 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.

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. (I don’t have the client-side testing part 100% figured out yet.)

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. I suggest you just copy my file and replace yours wholesale.

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 Yeoman’s Angular generator. (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. Just accept all the defaults.

Start Grunt:

It’s likely that you’ll the error “invalid option: –fonts-dir”. The solution (or at least solution) to this problem is to remove the following line from your Gruntfile.js (line 186 for me):
When it spins up free of errors or warnings, Grunt should open a new browser tab for you at http://localhost:9000/#/ 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

We’ll use something called grunt-connect-proxy to forward certain requests from our Grunt server running on port 9000 to our Rails server running on port 3000.

Change your Gruntfile to match this:

Now kill Grunt and again run:

You should now be able to go to http://localhost:9000/api/groups and get empty brackets. (Make sure your Rails server is running.) Our Angular app is now talking to Rails.

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 we’ll add an AngularJS resource that will allow us to conveniently perform CRUD operations on Group. AngularJS resources match pretty nicely with Rails resources, and I’ve found that my code can be a lot more DRY using Angular resources than jQuery AJAX. We won’t get into the details here of the Angular/Rails resource interaction, though. All we’ll use is the query() method, which matches up to a Rails resource’s index action. Add the Group resource to app/scripts/app.js. I changed more than just a few lines in this file, so you might want to just copy and paste the whole thing.

Now add a controller for the Group resource:

And add a view.

Lastly, add the following line to app/index.html, near the bottom:

If you now go to http://localhost:9000/#/groups, you should see our list of groups.


I’ve written a separate article about deployment called How to Deploy an Angular/Rails Single-Page Application to Heroku.

This is a work in progress

This tutorial is a work in progress, and I intend to update it as a) the technology moves and b) I learn more. I’m aware that there are certain things missing, such as testing on the client side and end-to-end tests. But right now I just want to get this tutorial out into the world because I haven’t seen anything yet that spells out the Rails/Angular combo with this level of hand-holding. I hope this has been helpful. Please leave me any questions or comments you might have. Thanks.

About the author

Jason Swett


Click here to post a comment