Angularize Your Rails App Part One: Routing
I’ve recently had the pleasure of converting the front end of our customer relationship management software, voltageCRM, into an AngularJS powered heavy client. Not only has it made UI development easier (and faster), but it also reduces the load on our servers. If you’re interested in gaining these benefits for your Rails app then read on.
AngularJS and Fat Clients
Let’s start with the basics. What exactly is a fat client? Loosely speaking, a fat client is one which provides functionality. A purely thin client would do nothing but display messages from and pass messages to the server. AngularJS provides tools and organizational structure for your client side code.
AngularJS certainly isn’t the only player in the game. Here you can find a comparison of to-do list software in various client side frameworks.
I had a tough time making my decision about which front end framework to use. In the beginning, I decided on Ember.js. What, did you think I was going to say I chose AngularJS? Actually, while I thought AngularJS seemed like the best framework I also found the least specific information on using it with Ruby on Rails (this is changing rapidly; even now a search reveals far more results than it did during my initial review). I found many tutorials on integrating Ember.js with Rails.
When you get right down to it, the problem was that I didn’t feel more productive in Ember.js. I never got excited by the features and thought “this is going to change the way I make web apps.” Perhaps I didn’t give it enough time, but I’ve spoken to others who felt the same. The end result is that I switched to AngularJS and that, despite some scope issues, I’ve found it to be extremely useful.
Where Do We Begin?
That’s up to you. This brings up another reason I like AngularJS. You can feel free to use as much or as little of it as you want. You can start the conversion in any place you want. I chose to start with client side routing. In our CRM I use a bit of JS to calculate the height of the window and vertically space the menu items on load. This process was being repeated every time a user clicked a link in certain browsers (turbolinks wasn’t working for some reason) which was distracting to say the least. By switching to client side routing we avoid reloading content unless necessary and thus solve the problem.
This tutorial uses CoffeeScript in place of JavaScript and Haml in place of HTML. If you don’t know these remarkable tools, don’t worry. It only takes a few minutes of reading the examples to become familiar enough to understand what’s going on. I have also taken the liberty of adding the HTML5 “data-“ preface to the AngularJS directives to make them compliant.
Routing
Let’s start with some code for the main module:
There’s a lot going on in this little bit of code so let’s look at it from the top. We’ve started by declaring a new variable as an AngularJS module named ‘todos’. Similarly, we’re going to add that name to the ng-app directive attached to the HTML element in our layout:
As you might have guessed, this tells AngularJS to use that module to manage our application. The ng-csp directive enables Content Security Policy support.
We’ve also declared two dependencies for our module in the passed array: todosServices and ngSanitize. The former will be a custom service we create for communicating with the server, while the latter is a AngularJS service (as indicated by the preface ng) used to sanitize output. Code and data in AngularJS are strictly scoped; dependencies must be declared before they can be used.
The third argument we pass to our module initialization would normally be a function. However, we’re going to pass it an array containing first the dependencies as strings followed by the function containing our route configuration. This practice of replacing functions with arrays containing the dependencies as strings is something we’ll continue throughout the application. It is a cure for the problems resulting from the minification Rails does during asset precompilation stage (AngularJS matches the parameter name to the dependency, and these names change during minification).
The first line of our routing configuration, $locationProvider.html5Mode(true);, specifies that AngularJS should use push-state to simulate real changes from page to page. This will result in the removal of that pesky hash mark (#) from your URL.
On the next line we tell the $routeProvider service to add a root (‘/’) route. We also pass in the URL where your template is located as well as the name of the controller to be placed in charge. In this case it is the template and controller for our application’s dashboard. I’ll go into more detail about creating these in the next post.
Finally, we add a config function which attaches the Rails Cross Site Request Forgery prevention token to every request sent by the $httpProvider service. Since this is the base service used to connect with our Rails application it will automatically include the CSRF token.
Phew! That’s a lot of material for a few short lines of code. I had intended for this to be a single-part tutorial but I believe I’m going to have to extend it. There is a lot more yet to cover. In the meantime you might wish to read the official tutorial (assuming you haven’t already). As usual I’m open to suggestions and corrections so if you have something you’d like to say then don’t hesitate to e-mail me. I’ll be posting part two of this “Angularize Your Rails App” series next Monday so stay tuned. Update: The next article, Separating Your Rails Assets, has been posted.