Building an Angular Application on a Rails Backend: Part 3
This is the third of a three part series about the insights I gained while building my first AngularJS application using Ruby on Rails in the back-end. This particular post is about setting up the front-end templates and interacting with the Rails back end using Angular factories. There are almost certainly better ways to do some of these things, but this is how I did it and what I learned.
After the last blog post, I have a basic Rails application back end with a basic Angular controller in place. At this point, the only data that exists within this controller is just a simple string variable put in place to test that there aren’t any errors with the setup.
The first step to complete is to include the modules we need in app.js
. This can be accomplished in a couple steps. First, since we’re using Rails, one of the needed modules can be packed into the application by adding gem 'angular-rails-templates'
to the Gemfile and running bundle install
. For the other two needed modules, I need to download the actual source code and save that into the app/assets/javascripts
directory. The modules I need to obtain are ui.router and ngMessages. As with any javascript files in Rails, I need to add all of those modules to the Asset Pipeline as well by making sure to add the following lines to the application.js
file:
//= require angular-ui-router
//= require angular-resource
//= require angular-rails-templates
Finally I can add each module to the initialization of the angular app in app.js
using the second bit of code below:
// Previous code
angular
.module('trackazon', []);
// New code with modules added
angular
.module('trackazon', ['ui.router', 'templates', 'ngMessages'])
It’s simple to determine if any errors have been made by starting up the server using rails s
, load the page in a browser, then check the browser’s console.
With no errors, I need to create a route and wire it up to a template and controller.
angular
.module('app', ['ui.router', 'templates', 'ngMessages'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('inventory', {
url: '/',
templateUrl: 'products/index.html',
controller: 'InventoryController as vm'
})
$urlRouterProvider.otherwise('/');
});
Here we have drawn a route called inventory
which is mapped to the url ‘/’. Since Angular will add a hashtag (#) to the end of the URL, this inventory route having a url of ‘/’ means that the URL in the browser will end with #/
. This route also has defined a template called ‘products/index.html’, which means I need to create a directory within app/assets/javascripts/
called ‘products’ and an ‘index.html’ file inside that new directory.
Also, since I set the controller for this route to ‘InventoryController’, I need to build a controller action which will kick off a process to access the products in the database. We already have this controller from the previous sections, but some stuff needs to be updated.
angular
.module('trackazon')
.controller('InventoryController', function($scope) {
var ctrl = this;
ctrl.products = [];
ctrl.getProducts = function() {
ProductService.getProducts()
.then(function(response) {
ctrl.products = response.data;
}, function(error) {
console.log("Error occurred: " + error);
})
}
});
Alright, so what just happened there is I set a variable ctrl
equal to the InventoryController so it’s easier to understand what scope I’m in later in the controller. Next I set a controller variable products
equal to an empty array so I can have a structure to hold data in later. Lastly I make a function whose job is to go out and get the products data from the database. But wait, you may notice that I used something called ProductService
, which I haven’t explained yet.
A service in Angular is a great way to extract interaction with a database or external source into it’s own home. In this case, the ProductService
will simply interact with the local database we created using Rails. Creating a service is fairly straightforward as you can see in the code below:
angular
.module('trackazon')
.service('ProductService',['$http', function($http) {
this.getProducts = function() {
return $http.get('/products.json');
}
}]);
With this code in place, our controller will now have all the products inside the variable ctrl.products
. So how do we display this? Well, since we set the templateUrl in the application router, we need to create that file. So, now I go ahead and make a file called index.html
located inside the app/assets/javascripts/templates/products
directory. Next, I need to edit that index.html
file to display some of the product attributes.
<ul ng-controller="inventory">
<li ng-repeat="product in inventory.products" id="" name="product.name">
, ,
</li>
</ul>
Since I already mounted the application to the html element, I needed to go ahead and declare which controller I’m using with the ng-controller
directive. With this defined, the products/index.html
file will be loaded into the browser DOM. The HTML in the example above uses an Angular directive ng-repeat which will iterate through all the items in a collection. In this case, it is goes through all the products and makes an instance variable product
which allows me to access various attributes which I can display on the page.
Now that everything is working and displaying on the page, it’s time to conclude this series. I hope you learned something, because I know I learned a lot while trying to explain how to create and Angular application.