June 7, 2016
Angular Integration with Rails
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.
- 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.
- 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.
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
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
- 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
- Add Angular, template helper, and the AngularJS app file structure to application.js
- Add a Rails view and controller for the landing page of AngularJS and set ng-app directive in view (
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.
- 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.
- 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.
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.
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.
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.
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.