rails + angular + bower

Rails provides an excellent backend for your Javascript front-end framework.

Why Rails?

Rails is a great lightweight, powerful server-side solution. You can add your front-end framework on top of it.

The Rails motto is “convention over configuration”. Rails provides a set of standards that the community has agreed upon as best practice. So if another Rails dev is checking out your app, it’s easier to get up and running.

Rails allows you to scaffold apps very quickly so it’s great for startups. It’s relatively painless to roll out a prototype.

  • e.g.- AirBnB, Hulu, Twitter, Blue Apron, Plated, Daily Burn, GA website, Robinhood FAQ, Death Wish Coffee (Intuit Super Bowl Ad)

Rails is “auto-magical”, which can be good or bad.

  • GOOD - rapid prototyping
  • BAD - Sometimes you don’t always know what’s going on under the hood, but as long as you play in their sandbox you’re ok (e.g.- use their helper methods, sset pipeline, etc).

Rails API

You may also use Rails for API-only Apps and scaffold a project with no views. This would allow you to host Rails on one server and your front end on another. This makes for an extremely lightweight Rails app since it takes out any middlewares and extra cruft that the views would normally use.

For a clunky analogy… the Rails API would be like a hard-top convertible in that we can completely remove the views. A soft-top convertible is like scaffolding a standard Rails app but ignoring the views (even though the functionality is always available).

Add Angular to a Rails app

  • CDN
  • Download angular files
  • Ruby Gem
  • Bower (what we’re using tonight)

Rails App Code Along

Completed code for this article can be found here. Credit goes to this excellent Angular-Rails tutorial. It was my reference for the initial Bower implementation.

1) rails new books -T --database=postgresql

This command will create a new rails project + directory called books. -T tells Rails not to scaffold a test suite. --database=postgresql will automatically bundle the gem pg so we may use a PostgreSQL database. By default Rails uses SQLite.

2) cd books && bin/rake db:create

This will cd into the project directory and create our database. Rake is Ruby Make, a standalone Ruby utility used for common administration tasks, especially sophisticated ones that build off of each other.

3) At this point, we can start our Rails server with rails server and checkout the welcome page at http://127.0.0.1:3000

Add and configure Angular + Bootstrap using Bower

Bower was created by Twitter specifically to manage front-end assets. We’ll use it to set up our front-end packages and dependencies.

4) Add gem 'bower-rails' to the Gemfile. From the command line, run bundle install to add the library.

Ruby gems are similar to npm modules, the Gemfile is similiar to package.json and bundle install is very similar to npm install

5) Next, on the command line, run touch Bowerfile. This will create a Bowerfile in the root of the project. Then add the following to that file:

# This will add Angular and Bootstrap-Sass
asset 'angular'
asset 'bootstrap-sass-official'

6) Run rake bower:install

Bower installs dependencies in vendor/assets/bower_components. The Rails asset pipeline dictates that 3rd party code lives in the vendor directory.

7) Let’s add the Angular require paths to app/assets/javascripts/application.js. Turbolinks is a gem that uses Ajax to speed up page rendering in most applications. We’ll remove turbolinks since it can hinder performance for front-end frameworks. Your requires should look like so:

//= require jquery
//= require jquery_ujs
//= require angular/angular
//= require_tree .

8) Let’s import our bootstrap stylesheets in app/assets/stylesheets/application.css.scss beneath all of the comments. Also, be sure to add the .scss extension to the file name so it will @import correctly:

@import "bootstrap-sass-official/assets/stylesheets/bootstrap-sprockets";
@import "bootstrap-sass-official/assets/stylesheets/bootstrap";

Add a doorway route for our Angular app

9) Add root 'home#index' to routes.rb. This will be the main route that will serve as the doorway to our Angular app.

10) Let’s create a controller for that route. Create app/assets/controllers/home_controller.rb and add:

class HomeController < ApplicationController
  def index
  end
end

Instantiate our Angular App

11) For this demo we’re gonna use a Book Model so we’ll call this our bookApp. Let’s create app/assets/javascripts/app.js and add code to instantiate our Angular app:

angular.module('bookApp',[])
 .controller('BooksController', BooksController);

  function BooksController() {
    var vm = this;
    vm.names = ["Marc", "Maren", "Diesel"]
  }

We’re using var vm here for view model. I’ve also included some dummy data to make sure our view is wired up correctly.

Create a view

12) Create app/assets/views/home/index.html.erb and add:

<div class="container-fluid" ng-app="bookApp">
  <div class="panel panel-success">
    <div class="panel-heading">
      
      <h1 ng-if="name">Hello, {{name}}</h1>
      
    </div>
    <div class="panel-body">
      <form class="form-inline">
        <div class="form-group">
          <input class="form-control" type="text" placeholder="Enter your name" autofocus ng-model="name">
        </div>
      </form>
    </div>
  </div>

  <div class="panel panel-success" ng-controller="BooksController as ctrl">
    <div class="panel-heading">
      <ul ng-repeat="book in ctrl.names">
        
        Hello, {{book}}
        
      </ul>
    </div>
  </div>
</div>

You could also add ng-app to the body tag in app/assets/views/layouts/application.html.erb. Then our entire app would belong to booksApp.

13) We can start up our rails server and go to localhost:3000 to make sure that Angular is working.

Create the Book model + add some seed data

14) rails g model Book title author

We’ll create a model for Book with title and author fields (both Strings, by default)

15) Let’s add some seed data to db/seed.rb to populate our database:

Book.destroy_all
Book.create([
  {title: "50 Shades", author: "Schmitty"},
  {title: "Lawyer Books", author: "Grisham"},
  {title: "The Hunger Games", author: "Collins"},
  {title: "JS Good Stuff", author: "Crockford"}
])

16) From the command line run rake db:migrate db:seed

This will create our schema file and seed our database

Set up our REST-ful api endpoint to serve the JSON

17) Add a route to our app/config/routes.rb file under our root route.

 get '/books' => 'books#index'

A route consists of the HTTP verb, the URL and the controller/action.

18) Eventually, we’re gonna use $http.get in our Angular controller to grab our data. Let’s create a controller to handle the JSON data from our API. Create a file in app/assets/controllers/books_controller.rb. Then, let’s make all the books available via JSON in our BooksController index action:

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end

19) We’re gonna use jbuilder to serve our books API data. Let’s create app/assets/views/books/index.json.jbuilder and add:

json.array!(@books) do |book|
  json.extract! book, :id, :title, :author
end

20) Now we can start our server rails server and go to http://localhost:3000/books.json (try it in a new window) to see that Rails is serving our data as JSON.

Consume the data with Angular

21) Let’s add some code to grab all the books from our endpoint in app/assets/javascripts/app.js

angular.module('bookApp',[])
 .controller('BooksController', BooksController);

function BooksController($http) {
  var vm = this;
  vm.names = ["Marc", "Maren", "Diesel"];
  
  vm.books = getBooks().success(function(data){
    vm.books = data;
  });

  function getBooks(){
    return $http.get('http://localhost:3000/books.json');
  }
}

We’ll add a getBooks() function to grab all the books from our REST-ful endpoint. We’ll assign the results of that function to the variable vm.books. Also, don’t forget to inject $http into BooksController.

Update our view

22) Let’s go to our app/assets/views/home/home.html.erb page and make some small tweaks. First, we’ll adjust our ng-repeat so it repeats our vm.book variable. We’ll also add some code to render the title and author of each book. Here’s the code for our ul:

<ul ng-repeat="book in ctrl.books">
  
  Title: {{book.title}} </br> 
  Author: {{book.author}}
  
</ul>

Conclusion

I really love Rails. It’s great to pair the rapid prototyping ability of Rails with an Angular front-end!