This guide will walk you through creating just about the simplest Angular + Rails application possible.
We’re going to create an Angular app, then create a Rails app, then get the two to talk to each other. It should only take a few minutes. Here are the steps:
Get set up
Get an Angular app initialized and running
The app we’re going to create is a pretend app called Home Library that exists for the purpose of organizing one’s private book collection.
We’ll be using the Angular CLI (command-line interface) to create the Angular app. Before we can do anything else, we need to install Angular CLI.
(If you don’t have NPM installed, you need to install that before installing Angular CLI.)
$ npm install -g @angular/cli
The Angular app and the Rails app will sit side-by-side in a directory. We’ll call this getting_started
although you could call it anything you’d like.
$ mkdir getting_started
$ cd getting_started
This is the command to initialize the Angular app:
$ ng new home-library
Once this command finishes, cd
into home-library
and run ng serve
.
$ cd home-library
$ ng serve
You should be able to visit http://localhost:4200
and see a screen that looks like this:
Initialize a Rails app
Now, in a separate terminal, create the Rails app and then create its database. (The –api flag means this will be an API-only Rails app.)
$ cd ..
$ rails new home_library --api -T -d postgresql
$ cd home_library
$ rails db:create
Prepare some data
Create a resource in the Rails app
Since our “Home Library” app is oriented around books, let’s create a resource called Book.
$ rails generate scaffold book name:string
$ rails db:migrate
Add some data to the Rails app
Let’s put a couple books in a seed file so we have some data to work with.
# db/seeds.rb
Book.create!([
{ name: 'Copying and Pasting from Stack Overflow' },
{ name: 'Trying Stuff Until it Works' }
])
$ rails db:seed
Connect Angular with Rails
Run the Rails server
In order for Angular to talk to Rails, the Rails server of course has to be running. Let’s start the Rails server.
$ rails server
You should see the good old “Yay! You’re on Rails” screen.
And if you visit http://localhost:3000/books.json
, you should see the list of books we just created.
Enable CORS so the Angular app can talk to the Rails app
There is a small amount of plumbing work involved to get our Angular and Rails app working. We need to enable CORS (cross-origin resource sharing).
Don’t worry if you’re not familiar with that term. In plain English, we have to tell Rails that it’s okay to let outside apps talk to it.
The first step is to uncomment rack-cors
in the Gemfile
.
# Gemfile
source 'https://rubygems.org'
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
"https://github.com/#{repo_name}.git"
end
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.0.2'
# Use postgresql as the database for Active Record
gem 'pg', '~> 0.18'
# Use Puma as the app server
gem 'puma', '~> 3.0'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
# gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'
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
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]
$ bundle install
Then make config/initializers/cors.rb
look like this:
# config/initializers/cors.rb
# Be sure to restart your server when you modify this file.
# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept
# cross-origin AJAX requests.
# Read more: https://github.com/cyu/rack-cors
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*',
headers: :any,
expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'],
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
And remember to restart the Rails server after you do that.
Get Angular to talk to Rails
We’re almost done. One of the last steps is to add an HTTP request from the Angular app to the Rails app.
Modify src/app/app.component.ts
to look like this:
// src/app/app.component.ts import { Component } from '@angular/core'; import { Http } from '@angular/http'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app works!'; books; constructor(private http: Http) { http.get('http://localhost:3000/books.json') .subscribe(res => this.books = res.json()); } }
This will handle getting the data from the Rails server. Now we need to show it on the page.
Show the data from Rails inside the Angular app
Modify src/app/app.component.html
to look like this:
<!-- src/app/app.component.html --> <h1> {{title}} </h1> <ul> <li *ngFor="let book of books">{{ book.name }}</li> </ul>
Now, if you refresh the page at http://localhost:4200
, you should see our two books on the page:
Congratulations. You’re now in possession of an Angular app that talks to a Rails app.
Hey there,
I’m brand new to Angular (as in just installed it tonight), and have only worked with Rails for a little while, so this was a great tutorial, thanks so much!
I got 99% of the way through, with everything working, until the final step… Angular doesn’t display the book titles, just a blank page… didn’t see any errors in either the ng serve or rails server consoles, and I’m not sure where else to look? Any further help would be greatly appreciated.
Hey there. Maybe i can help
I had to change a few things to make it all work.
the files that i changed are:
app.component.ts,
app.module.ts,
app.component.htm
book.ts (New file, just defines the book class, with one property: name.)
Notice the following:
Replacing Http with HttpClient
Using the new book model inside the http request
APP.COMPONENT.TS
import { Component } from ‘@angular/core’;
import { HttpClient } from ‘@angular/common/http’;
import { Book } from ‘./_models/book’;
@Component({
selector: ‘app-root’,
templateUrl: ‘./app.component.html’,
styleUrls: [‘./app.component.css’],
providers: [HttpClient]
})
export class AppComponent {
title = ‘books’;
books: Book[];
constructor(private http:HttpClient){
this.http.get(‘http://localhost:3000/books.json’)
.subscribe((res : Book[]) => this.books = res);
}
}
BOOK.TS
export class Book{
constructor(public name:string ){ }
}
APP.MODULE.TS
import { BrowserModule } from ‘@angular/platform-browser’;
import { NgModule } from ‘@angular/core’;
import { NgbModule} from ‘@ng-bootstrap/ng-bootstrap’;
import { HttpClientModule} from ‘@angular/common/http’;
import { AppComponent } from ‘./app.component’;
import { BookComponent } from ‘./_models/book.component.trs’;
@NgModule({
declarations: [
AppComponent,
BookComponent,
],
imports: [
BrowserModule,
NgbModule.forRoot(),
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
APP.COMPONENT.HTML
{{book.name}}
i got a error on the line
import { BookComponent } from ‘./_models/book.component.trs’;
do u have a clue what is going on ?
I’m new to Angular so take this with a grain of salt, but I believe I figured this out.
After completing the tutorial, inspecting the page shows a JS error with the Http app component. So, I only kept jmarsh’s HttpClient updates, and that seemed to work fine. I did have an issue with the single quote characters from jmarsh’s code that I had to replace.
After that I just got the following error in the console: “ERROR TypeError: res.json is not a function”, which I resolved by dropping the call to .json(). The following was my final code:
APP.COMPONENT.TS
import { Component } from ‘@angular/core’;
import { HttpClient } from ‘@angular/common/http’;
@Component({
selector: ‘app-root’,
templateUrl: ‘./app.component.html’,
styleUrls: [‘./app.component.css’],
providers: [HttpClient]
})
export class AppComponent {
title = ‘app works!’;
books;
constructor(private http:HttpClient){
this.http.get(‘http://localhost:3000/books.json’)
.subscribe(res => this.books = res);
}
}
APP.MODULE.TS
import { BrowserModule } from ‘@angular/platform-browser’;
import { NgModule } from ‘@angular/core’;
import { HttpClientModule} from ‘@angular/common/http’;
import { AppComponent } from ‘./app.component’;
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Working solution! Thank you so much!
My apologies. I meant to reference “matt’s code”, instead of “jmarsh’s”, in my previous comment.
This is wrong and the comments are wrong also
Nice tutorial. But, I’m think about if the architecture is good on production. Is it good to have a node server rendering the front end rather than just compiling it into a JS file and rendering it from the Rails server?
Thanks, and good point. When I deploy to production I do it differently. See these two articles:
https://www.codewithjason.com/deploy-angular-2-rails-5-app-heroku/
https://www.codewithjason.com/deploy-rails-application-angular-cli-webpack-front-end/
how to reroute angular routes with rails?