Angular 2 and Ruby on Rails on Heroku

rails5-angular2

In this tutorial, you will learn how to successfully integrate Angular2 with Rails 5 and to deploy the application to Heroku.

There are multiple ways to structure a project with Angular2 and Rails 5. One method is to put your Angular2 project within the Rails application, use the Rails views, and have the CSS and javascript assets go through the asset pipeline. The method I chose to use is to completely separate the front-end Angular 2 SPA with the back-end Rails API. Angular 2 is still new and there hasn’t been a good way to fully integrate Angular 2 with Rails. Keeping them separate allows for changes to be made within the framework and not having any breaks. It’s also simpler since we will be doing a single deployment for both the front-end and the back-end.

This tutorial assumes you have a basic understanding of Ruby on Rails and Angular 2 (which is quite different from Angular). If not, I recommend Michael Hart’s Ruby on Rails tutorial and the official Angular 2 tutorial. Let’s get started!

Setting up the environment

The first thing we are going to do is create a Rails app running Angular 2 and successfully run a local server.

Create your Ruby on Rails project

Create your Ruby on Rails application by running the following command:

rails new myapp --database=postgresql

It is up to you whether or not you choose to use api mode for the Rails application. I chose not to use it because I am planning on making more views rather than just an SPA. We add the postgresql flag in order to deploy our Rails application on Heroku. The official Heroku documentation can be found here.

Run ‘bundle install’

Run bundle install to download the necessary gems. I added some gems that created a Javascript runtime with the application. My Gemfile looks like this:

source 'https://rubygems.org'

ruby '2.3.0'

gem 'execjs'
gem 'rails', '~> 5.0.1'
gem 'pg'
gem 'puma', '~> 3.0'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.2'
gem 'jbuilder', '~> 2.5'
gem 'jquery-rails'
gem 'rack-cors'
gem 'sprockets'
gem 'therubyracer'
gem 'turbolinks', '~> 5'

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platform: :mri
end

group :development do
  # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.0.5'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]</pre>

Start the server

Start the server and see if everything works!

rails s

Integrate Angular 2 with Ruby on Rails

Now here comes the tricky part. Rails uses the public folder to store all assets that go through the asset pipeline and static assets in production. We’re going to use this folder to give Rails our Angular 2 assets as well.

Download angular-cli

Angular-cli is a command line tool to help with setting up, building, and deploying Angular 2 applications. In order to install angular-cli, you need to have Node.js 4 or higher, along with npm 3 or higher. You can download Node.js here.

Install angular-cli with this command:

npm install -g angular-cli

Create an Angular 2 project

We’re going to create our Angular 2 project in a new directory within our Rails application. We are going to name this folder clientThis folder must be named client for Heroku deployment reasons I will go through later.

Navigate to the root of your Rails application and create your new client Angular 2 application with this command:

ng new client

Test if the Angular 2 project works by  running this command and then navigating to http://localhost:4200/.

cd client
ng serve

Create a symbolic link for the public folder

Now that we have our Rails server and Angular2 server working, we need to integrate the two. Start by building your Angular 2 application (we’re going to use production to simulate a Heroku environment).

ng build --prod

This will build our Angular 2 application and store the artifacts in the client/dist folder. These are the assets that need to go into the public folder so that Rails knows to use these assets.

However, we don’t want to move our build artifacts to the public folder every time we deploy. We can fix this by deleting the public folder and replacing it with a symbolic link to our client/dist folder.

rm -rf public
ln -s client/dist

Run the server locally

Start your Rails server, and you should see you “app works!”. Congratulations, we successfully integrated Angular 2 with Rails locally!

rails s

Deploy to Heroku

The final step is to deploy our application to Heroku. This is also a tricky step since we have to customize the build process to build our Angular 2 application first and then handle our Rails application.

Setup git

First order of business is to setup git. You can do this with the following command:

git init

I would highly recommend to remove packages and build files from the repository. I added these statements to my .gitignore file:

# angular 2
client/node_modules
client/dist

Create the Heroku App

Create the Heroku application with the following command:

heroku create

Add Node and Ruby buildpacks

If we were creating a normal Rails application, Heroku would auto-detect Ruby and use the heroku/ruby buildpack. However, we need Heroku to install our Node packages build our front-end application using the Node buildpack.

We can tell Heroku about the buildpacks we want to use with the following command:

heroku buildpacks:add https://github.com/jasonswett/heroku-buildpack-nodejs
heroku buildpacks:add heroku/ruby

The order matters. If we put the ruby buildpack first and the Node buildpack second, Heroku would give us a single Node dyno that would not know how to run ruby. We put the ruby pack second to tell Heroku to give us two dynos, a web and worker dyno, that both know how to run ruby.

We use this particular Node buildpack instead of Heroku’s default buildpack because we need to tell Heroku that the package.json resides in the client directory. This is why it was important to make sure that the folder the Angular 2 app resides in was called client.

Modify the package.json

Modify your package.json to look like this:

{
  "name": "client",
  "version": "0.0.0",
  "license": "MIT",
  "angular-cli": {},
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "lint": "tslint \"src/**/*.ts\"",
    "test": "ng test",
    "pree2e": "webdriver-manager update",
    "preinstall": "npm install -g node-gyp",
    "heroku-postbuild": "ng build -prod",
    "e2e": "protractor"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "^2.3.1",
    "@angular/compiler": "^2.3.1",
    "@angular/core": "^2.3.1",
    "@angular/forms": "^2.3.1",
    "@angular/http": "^2.3.1",
    "@angular/platform-browser": "^2.3.1",
    "@angular/platform-browser-dynamic": "^2.3.1",
    "@angular/router": "^3.3.1",
    "core-js": "^2.4.1",
    "rxjs": "^5.0.1",
    "ts-helpers": "^1.1.1",
    "zone.js": "^0.7.2",
    "@angular/compiler-cli": "^2.3.1",
    "@types/jasmine": "2.5.38",
    "@types/node": "^6.0.42",
    "angular-cli": "1.0.0-beta.24",
    "codelyzer": "~2.0.0-beta.1",
    "jasmine-core": "2.5.2",
    "jasmine-spec-reporter": "2.5.0",
    "karma": "1.2.0",
    "karma-chrome-launcher": "^2.0.0",
    "karma-cli": "^1.0.1",
    "karma-jasmine": "^1.0.2",
    "karma-remap-istanbul": "^0.2.1",
    "protractor": "~4.0.13",
    "ts-node": "1.2.1",
    "tslint": "^4.0.2",
    "typescript": "~2.0.3"
  },
  "engines": {
    "node": "6.9.3",
    "npm": "3.10.10"
  }
}

We do several things here. First, we move all of our devDependencies into dependencies because Heroku will not include those packages in a production environment. Second, we add the preinstall and heroku-postbuild hooks to tell Heroku about our build process. Before we install our packages, we want to install node-gyp to help compile our packages. After we build our packages, we want to run ng build -prod to compile our Angular 2 application to the client/dist folder and serve them to Rails.

Deploy to Heroku

After all of our changes, we should be ready to deploy to Heroku using this command:

git add .
git commit -m 'heroku push'
git push heroku master

If the deployment goes well, open the app using this command:

heroku open

You should now see your Angular 2 application running on a Rails server on Heroku!

8 thoughts on “Angular 2 and Ruby on Rails on Heroku

  1. Hi! thanks for your post. I have an issue – if i reload page on some site page (not index page), i have nothing to display. How i can customize heroku webserver for produce any request by index.html?

    Liked by 1 person

      1. He said, if you reload some pages, wich has params[:id] in URL, the rails router didn’t found any templates in views. I’ve resolved this problem just added a controller with empty actions. But I have got a new problem. When I trying to open URLs with params, for example, ‘www.example.com/post/1’ Heroku server is not responding. Maybe you had a similar problem and know, how to resolve it?

        Like

  2. Nice guide. On some of those commands when switching between the Rails and Angular app, it’d be helpful to tell people where to run the commands.

    Also, if you’re removing `public` and replacing it with a symlink to the Angular app’s distribution folder, use:

    ln -s client/dist public

    Like

  3. I’m getting an error when pushing the code to Heroku to build. I’ve spent all day on this,any advice would be greatly appreciated!

    This is what’s being returned by Heroku before it crashes

    remote: —–> Preparing app for Rails asset pipeline
    remote: !
    remote: ! File exists @ dir_s_mkdir – public
    remote: !

    Like

  4. Hi! Very nice guide. You write: I chose not to use it because I am planning on making more views rather than just an SPA. Can you show an example how you do that, for example with Angular 2 or 4?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s