When it comes to Angular and Rails, there are many ways to set up a project. However when starting out, all these options can be a tad overwhelming…
Over the last few months, I’ve been focusing on project set-ups and have come to find a few favorites. In this post, I’ll illustrate one way to use Rails with Angular (see Github links with the example code below).
Create a New Rails App
To get started lets create a new rails app.
gem install rails -v 4.1 rails new angular_example
In this app, I chose not to use turbolinks. It’s possible to use Angular with Rails and turbolinks, but the Angular app bootstrap process is more complex. I find that turbolinks and Angular serve the same purpose, which is to make the app respond faster. Adding turbolinks, in addition to Angular, adds a lot of complexity and not much benefit.
Remove Turbolinks
Removing turbolinks requires removing it from the Gemfile.
gem 'turbolinks'
Remove the require from app/assets/javascripts/application.js
//= require turbolinks
Add AngularJS to the Asset Pipeline
In order to get Angular to work with the Rails asset pipeline we need to add to the Gemfile:
gem 'angular-rails-templates' gem 'bower-rails'
Next, we can install these gems into our bundle.
bundle install
We added bower so that we can install the AngularJS dependency.
rails g bower_rails:initialize json
When adding Angular to bower.json, I prefer to specify the most recent version rather than going with the “latest”. This avoids having your app randomly break when a new version is released and your bower installs again.
{ "lib": { "name": "bower-rails generated lib assets", "dependencies": { "angular": "v1.2.25" } }, "vendor": { "name": "bower-rails generated vendor assets", "dependencies": { } } }
Now that bower.json is setup with the right dependencies, let’s install them:
bundle exec rake bower:install
Organize the Angular App
At this point we’ll create the following folder structure in app/assets/javascript/angular-app:
templates/ modules/ filters/ directives/ models/ services/ controllers/
This structure is only a convention and is not enforced by Angular (unlike file naming and organization in Rails). This project structure allows for a single web app to be easily composed of multiple smaller Angular modules rather than one giant Angular app for the whole site.
In app/assets/javascripts/application.js add Angular, the template helper, and the Angular app file structure. That file should now read like this:
//= require jquery //= require jquery_ujs //= require angular //= require angular-rails-templates //= require angular-app/app //= require_tree ./angular-app/templates //= require_tree ./angular-app/modules //= require_tree ./angular-app/filters //= require_tree ./angular-app/directives //= require_tree ./angular-app/models //= require_tree ./angular-app/services //= require_tree ./angular-app/controllers
Bootstrap the Angular App
Next, we’ll setup the Angular app bootstrapping. Create app/assets/javascripts/angular-app/app.js.coffee:
@app = angular.module('app', [ # additional dependencies here, such as restangular 'templates' ]) # for compatibility with Rails CSRF protection @app.config([ '$httpProvider', ($httpProvider)-> $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') ]) @app.run(-> console.log 'angular app running' )
Then, we’ll create an Angular module at app/assets/javascripts/angular-app/modules/example.js.coffee.erb
@exampleApp = angular .module('app.exampleApp', [ # additional dependencies here ]) .run(-> console.log 'exampleApp running' )
Following the Angular module, we’ll create an Angular controller for this app at app/assets/javascripts/angular-app/controllers/exampleCtrl.js.coffee
angular.module('app.exampleApp').controller("ExampleCtrl", [ '$scope', ($scope)-> console.log 'ExampleCtrl running' $scope.exampleValue = "Hello angular and rails" ])
Now we need to add a route to Rails to pass control over to Angular. In config/routes.rb:
Rails.application.routes.draw do get 'example' => 'example#index' end
Next, we’ll generate the Rails controller to respond to that route:
rails g controller Example
In app/controllers/example_controller.rb:
class ExampleController < ApplicationController def index end end
In the view we need to specify which Angular app, and which Angular controller will drive this page.
In app/views/example/index.html.erb:
<div ng-app='app.exampleApp' ng-controller='ExampleCtrl'> <p>Value from ExampleCtrl:</p> <p>{{ exampleValue }}</p> </div>
To view the app, start your Rails server and visit http://localhost:3000/example
You can find the code generated here https://github.com/rawsyntax/rails-angular-example