Angular Integration with Rails

The purpose of this blog is to describe how an Angular app can be set up in an existing Rails application. It is a challenge to organize the code and integrate two technologies so that Rails and Angular can talk to each other as an SPA. Most of the documentation/blogs on AngularJS setup revolves around JavaScript and Grunt, and it’s very hard to see how Grunt can be introduced in a Rails application where Sprockets already exists for similar tasks.

I explored two approaches for integration and both have their own pros and cons:

  • Leveraging Rails asset pipeline
  • Standalone angular setup using Grunt

Leveraging Rails asset pipeline

This approach is suitable if only a part of the Rails application views need to be migrated into Angular with minimal changes in the deployment process.

Pros

  • Seamless integration with the Rails asset pipeline with the help of various gems.
  • The Rails ERB template can be used in an Angular project so that both all of the Rails controller variables and the helper method can be availed in the template.
  • Deployment is easy – as all setup is – via Rails, so there is no need for any other run time environment or the JavaScript task runner for Angular project.

Cons

  • This is tightly coupled with the Rails application and if too many Rails helper methods are used in the Angular app, then scalability will be a concern due to difficulty with code segregation in Rails and Angular apps.
  • External libraries managed by Bower are in the Rails vendor folder, while JavaScript and CSS are scattered in an assets folder at different places. This means that the Angular app assets are scattered across the Rails application, which will be difficult to manage.

Setup

1. Update GemFile with the following gems and install them:

  • gem ‘angular-rails-templates’ – This adds Angular templates to the assets pipeline and adds the compiled templates to $templateCache. This gem can compile .erb, .slim, .haml, .str, and .md templates and has a default configuration that can be overridden in application.rb. More details on this can be found at this link.
  • gem ‘bower-rails’ – This is a Ruby wrapper for Bower that supports both the Bower standard package format (bower.json) and the Ruby DSL configuration at the project root file ‘Bowerfile’.

2. Install Bower: rails g bower_rails:initialize

3. Add all desired dependencies in Bowerfile

rails_1

4. Install dependencies by rake task bundle: exec rake bower:install

5. Add the Angular project skeleton

  • If the project is small and there are only handful of controllers and views in it, then this directory structure make more sense
    rails_2
  • To build a scalable and maintainable AngularJS app, an ideal app structure should be modularized into a very specific function. As an example, we created three modules (common, parent, and school); the common module can be used across all functions, but all function specific code should go in its respective modules
    rails_3
  • Add Angular, template helper, and the AngularJS app file structure to application.js
    rails_4
  • Add a Rails view and controller for the landing page of AngularJS and set ng-app directive in view (<body ng-app="TestApp">)
  • Turbolink can be disabled if no other Rails view need them. If you want to keep Turbolink enabled then you will have to do a re-bootstrapping of your app after the new page content is loaded in. Turbolinks has an event for this named page:load, which we can bind to after we define our Angular app. If we’re bootstrapping our app in our JavaScript, we need to remove the ng-app bootstrap directive from our HTML
    rails_5

Deployment

No extra setup is needed for AngularJS app deployment in this case; instead, the Rails asset pipeline takes care of the concatenation and compression of all AngularJS assets like JavaScript, CSS, and fonts. The only task for the Angular app that needs to be added in the build process is running its test cases. We were using Jasmine for writing test cases for Angular apps and the “Jasmine” gem was used to run JavaScript test cases. Now, the “rake jasmine:ci” task need to be added into the build process.

Standalone angular setup using Grunt

This approach is the best choice if the plan is to migrate all existing Rails views to AngularJS. Rails applications used for serving the API and Angular app will work as an API client. When we gradually start moving existing Rails views to the Angular app, then we want to keep a single deployment of the application in two cases:

  • We still want to serve some views from Rails and the rest from Angular.
  • We have moved all of the views to Angular and Rails is serving the API, but we don’t want to manage two separate application deployment processes and don’t see any scalability concern in near future.

Pros

  • Both code bases are segregated from each other, so each application can be treated as an individual entity and can be separated out without any hassle if you plan to scale your application and want to deploy both applications separately – i.e. Rails as an API application and the Angular app as an API client.
  • All code setup for AngularJS like external libraries, node modules, CSS, and images are not mixed up with Rails application assets.

Cons

  • Integration of both code bases is a challenge, and we need to manage two different deployment approaches in single deployment script.
  • Implementation is not in the “Rails way,” so initial setup might be a hassle.

Setup

1. Create a ‘client’ folder parallel to the Rails ‘app’ folder and setup the Angular app directory structure similarly to what we discussed in first approach.

rails_6

2. Add all required NodeJS packages in package.json and install those packages.

3. Add the required library, framework, and assets packages in bower.json and install Bower dependencies.

4. Remove the content from the Rails’ public folder; this folder content will be replaced by the output of the Grunt build.

5. There can be many ways to organize assets in a public folder: either all Grunt-generated assets can be added directly to the public folder or a specific app folder can be created in the public folder, which contains all Grunt-generated assets.

6. Change the line dist: ‘dist’ to dist: ‘../public’ under the var appConfig section of client/Gruntfile.js.

7. Add the public directory to .gitignore to remove it from version control.

8. In development environment, Grunt build can be executed manually but in production this process has to be automated.

Deployment

There are two approaches for deploying this kind of Rails application. We can either run Grunt locally and upload the public folder to a remote server or run the Grunt build on a remote server. With the former approach, the installation of Node and Grunt on a remote server can be easily avoided. I would prefer the former approach of building locally and copying to remote because that can be written in a custom Capistrano deployment task.

If the deployment is on Heroku, we can leverage multiple buildpacks, with each buildpack handling Rails and Angular deployment separately. This article can be useful for setting up buildpacks.

Conclusion

You need to determine which approach is suitable for you. Our advice is that if you are building an application for the long haul, you should take the standalone setup to leverage the best of both frameworks. This also keeps your options open to move to a micro-services architecture if the need arises in the future.

Tarun Kumar

Tarun Kumar

Senior Technical Lead

Tarun Kumar is a Senior Technical Lead for 3Pillar Global. He has over 8 years of experience in design, development, and support for various high-traffic, real time web applications across various domains using both Open Source and Microsoft technologies. He also has expertise in agile development methodologies and test driven development (TDD). Prior to joining 3Pillar, he worked for GlobalLogic and a technology start-up.

One Response to “Angular Integration with Rails”
  1. Ashwini on

    Hi Tarun, can you please elaborate more on having a hybrid system? I am trying to render views from Rails side sometimes because I do not wanto get into the trouble of building entire frontend in Angular.

    Reply
Leave a Reply

Related Posts

AngularJS: Understanding Directive Scope In the previous post, we created custom AngularJS directives. However, we did not address how directives manipulate data objects that are defined in t...
When to Take a Hybrid Approach for Mobile App Development We recently started working on the idea of developing a mobile app for one of our clients, and we faced a familiar question: whether to go with a nati...
Building Rails Apps and Deploying to Heroku Cloud with Jenki... Who should read this article? If you are interested in continuous integration or love to use the best tools available to get the most out of the dev...
3 Cloud Optimization Projects That Will Pay for Themselves i... AWS introduced 1,430 new features and tools in 2017, including 497 in the 4th quarter alone. This means that it can be a challenge for even the mos...
The Connection Between Innovation & Story On this episode of The Innovation Engine, we'll be looking at the connection between story and innovation. Among the topics we'll cover are why story ...