This is part one of several more parts regarding this topic. I will post at one-week interval for this series
[leadp]I'm a top-down person. So, whenever I try to understand or create a new concept, I like to climb my way up and see the views from above first before making any decisions. Therefore, when I first intended to create a single page application (SPA) with Angular as its front-end and Rails as its back-end, I'd love to talk about the structure first.[/leadp]
Source code for application that we will create, can be accessed here
Before, I go any further, I want to explain on why I use Rails as an API back-end. You may want to argue that Rails isn't lightweight enough for that purpose and I agree, performance isn't exactly one of Rails strong suit. However, other than the obvious reason that my team is already familiar with it, Rails have some advantages that I find it hard to substitute.
First is the thousands (or tenth of thousands) gems, which is readily available in Rubygems. I believe Rubygems is the perfect example of an excellent code-reuse platform. I love how easy it is to integrate my Rails application with those gems, which have significant effect in reducing boilerplate from my application. Oh, and have I mention how vibrant the community that supported those gems?
Second, I like the "Rails-way". I know how opinionated it is, but that's OK with me because I tend to agree with most of its opinion. Although, I can relate to people whom disagree, because they will find it very hard to use Rails and then they have to customize the hell out of it. Well as for me, it enables me to "write less for more" and due to my RSI, less for more is very important.
Starting new Rails application
Ok, enough with the chit chat, now we can continue. First, let's observe initial Rails application structure created by the
rails new command.
rails new project_one
project_one -- app ---- assets ---- controllers ---- helpers ---- mailers ---- models ---- views -- config -- db -- doc -- lib -- log -- public -- script -- test -- tmp -- vendor
Rails is a complete web application framework with MVC pattern, thus naturally it has their own templating system. Rails templating system uses .erb format by default, but you can customize it easily with other "aftermarket" solutions, such as the 'cute' .haml. Templates go into 'views' folder that you can see above (I believe view from V in MVC for Rails is not the same with this views folder, but let's get to it sometimes later).
If we want to use Angular and create SPA, Rails templating system won't be used much because Rails as an API-backend will (mostly) only spout JSON. Therefore I usually only fill this 'views' folder with default application layout and one 'blank' page. With the exception of situations where I have to provide some complex JSON formats and have to use rabl or jbuilder gems.
This 'blank' page mentioned above corresponds with one controller that will serve the Angular application landing page. Let's create it inside the project that we just made.
You may notice that I use the options
- Modify root route to the index action within home controller, which we have just created.
ProjectOne::Application.routes.draw do root to: 'home#index' end
- Don't forget to remove the default index.html in public.
- Modify the default application.html.erb file that will house our Angular application. You may want to take particular attention at the opening html tag, because now it contains
ng-app="app", which will tell Angular to serve an "app" application here.
Angular application can be placed almost anywhere and this is the beauty of it. You can "plug-and-play" an angular application to your existing web application using this directive inside any html tag
- Now, make sure that the controller index template, which will be yielded in
<%= yield %>above is available and empty.
cat > app/views/home/index.html.erb
don't forget to press ctrl+c immediately after typing the above command.
Including Angular Libraries
Next, we have to include Angular libraries into our Rails application. We can use gems for this purpose, but I prefer to include the corresponding js files manually and taking advantage of Rails asset pipeline. Asset pipeline is a system within Rails that 'automagically' combines and compiles all assets into browser-readable formats (such as .erb or .haml to html) during deployment process so that it can be opened in browser effortlessly. You can put your assets into any of three locations that was created by default. This is taken straight from their documentation:
lib/assets is for your own libraries’ code that doesn’t really fit into the scope of the application or those libraries which are shared across applications.
Rails asset pipeline is activated by default, but to make sure you can check it out at the
module ProjectOne class Application < Rails::Application ... # Enable the asset pipeline config.assets.enabled = true ... end end
So let's download latest angular library and put it inside the
As of today, Angular has stable version of 1.0.5. You can check the latest version here and update the above command as necessary. I also prefer to download the uncompressed files for easier debugging whilst Rails can helps in minifying the files later during deployment.
However, only putting the latest angular js files won't make it work, so let's modify the manifest file that list all js files that will be included in the Rails asset pipeline (if you don't specify it here, it won't be included by Rails). Rails already created this file and fill it with some initial configuration, but I prefer to replace it with my own configuration.
//= require angular //= require_self
As you can see above, I don't think that I need jquery for now, so I remove it.
requiretree . is also a bit tricky so I left it out and replace it with requireself.
Initializing Angular Application
ng-app="app" that written on our html block, we have to activate it using
angular.module. Before doing this I created a special folder within the
I will give detailed explanation regarding the structure later, but for now we will put
main.js.coffee file. This file will be the starting point of our angular application.
# Create 'app' angular application (module) @app = angular.module("app", )
Additional Steps (Optional)
You may want to include some additional gems that relate to assets, such as support for .haml, .sass and coffeescripts format. I provide you with an example of how my assets group looked like within the
Don't forget to
bundle after every
Looking back at the structure, I tried to model it as close as possible to Rails structure.
First I want to explain the 'config' folder, this folder contains all configurations and initializers. Some of examples on what I put in this folder is the
constants.js.coffee file. This file contains application-wide constants to be used by the angular application. I also have
debug.js.coffee file which stores some variables for easier debugging. Also, don't forget the all-important
routes.js.coffee that stores the routing of our client-side application.
# Prefix url string for api calls app.constant "apiPrefix", "/api" # Location for session information within sessionStorage # Related to authentication & session, will get back to this later app.constant "sessionStore", "_starqle_session"
app.run ($rootScope, $state, $stateParams) -> # You can turn this off on production. $rootScope.$debugMode = "on" # "off" # Capture current state and stateParams, this variable can be showed # in browser for debug purpose. $rootScope.$state = $state $rootScope.$stateParams = $stateParams
You may notice the $state variable in
debug.js.coffee above, this code is related to routing and I will get back to it later on part 2. I will also gives example on my implementation of
And if you use Rails, you must not forget to automatically set csrf authenticity token in all requests so that the request won't be rejected by Rails. I also put this
app.config ($httpProvider) -> authToken = $("meta[name=\"csrf-token\"]").attr("content") $httpProvider.defaults.headers.common["X-CSRF-TOKEN"] = authToken
config folder, there is
controllers folder. This folder contains, well, controllers for our angular application. There is also
directives folder. The ability to place custom attributes that can be controlled in separated files gives our html template 'steroid' that makes them more powerful in web application context, while keeping it uncluttered. Angular comes with many home-ground directives that you may find interesting, you can check it out here.
filters directory in which you can put your custom filters here and then there are also
services folders. You can see resource on angular like model in Rails, the difference lies on its datasource. If Rails application model glue the application with database, Angular resource is the interface between the application itself and external API that will be used as the datasource (which, in this case, Rails that spout JSON). Lastly, I see service as 'global' function that can be injected and used anywhere within the angular application.
To make sure that everything is working, you can just start your rails server and point your browser to the application url. If you see something like this when you see your page source using firebug...
class="ng-scope" means that your angular application has been initialized.
Ok, that concludes the first part, as you can see up to this point this writeup is (perhaps) not very practical. But practical is not my intention from the beginning, because I want to take different angle on explaining things, such as talking a bit more about concept and architecture. So let's continue with part 2 next week :)
Source code for application that we have created, can be accessed here