Better Resource Mapping with Ionic and AngularJS
One of our recent projects here at Philosophie NYC was working with the awesome team at Neighborhood Trust Financial Partners (NTFP) to build a mobile application to help users manage their ongoing cash flow. NTFP needed a custom administrative backend where financial advisors could chat with and guide users towards their financial goals. Users, in turn, would be able to work with NTFP via an iOS HTML5 web application.
For our tech stack, we decided on using Ruby on Rails to serve as both the administrative backend as well as an API for the frontend, with the relatively new Ionic framework powering the client-side component of the application. Having two separate applications — one in Rails (server-side) and one in Ionic (client-side) — presents interesting design challenges. This article will be focusing on how we were able to create a better one-to-one mapping between resources in both applications, without having to patch our own solutions together on a case-by-case basis.
Essentially, given that we had a Bank model on the server-side, on the client-side we wanted to be able to call something like:
Bank.get({ id: 456 }).then(function(response) {
// …
});
…and have that map to a RESTful Banks API on the Rails side of things, creating a less brittle and easier-to-maintain codebase. Ionic comes with out-of-the-box support for AngularJS, so we found a great resource factory gem by the team at FineLine Prototyping called angularjs-rails-resource that we used to achieve this goal.
Installing AngularJS Rails Resource is a straightforward process. Seeing as we are just adding the JavaScript component of the gem to our Ionic application rather than installing the full Rails gem for use with Rails’ asset pipeline, we simply need to pull the angularjs-rails-resource.js script into our application to install. This can be done either via a standard script tag:
<!-- www/index.html --><script src=”path-to-script/angularjs-rails-resource/angularjs-rails-resource.js”></script>
…or via Bower:
// bower.json{
“devDependencies”: {
“angularjs-rails-resource”: “~2.0.0”
}
}
And don’t forget to register the angular module itself:
// www/js/app.jsangular.module(‘testApp’, [‘ionic’, ‘rails’, ‘angular-storage’, testApp.controllers’, ‘testApp.services’])
So now on client-side of the application we can set up our models as such:
// www/js/models/bank.jsangular.module(‘testApp.models’).factory(‘Bank’, function(railsResourceFactory) {
var service = railsResourceFactory({
url: ‘/api/v1/banks’,
name: ‘bank’
}); return service;
});
On the Rails side, this setup means we can stick to some very vanilla Rails code in our API, allowing us to focus more on other areas of the application to deliver more client value. In this example, on the Rails side we can have a simple setup allowing the server and client-side to communicate effectively:
# routes.rbnamespace :api do
namespace :v1 do
resources :banks
end
end # app/controllers/api/v1/banks_controller.rb# ...def show
@bank = Bank.find(params[:id]) if @bank
render json: @bank, status: :ok
else
render json: {}, status: :not_found
end
end# ...
Back to the client-side, with the Bank model injected as a dependency, we could call things like:
Bank.get(456).then(function(response) {
// ..
});var bank = new Bank({
institution: ‘Wells Fargo’
});bank.save().then(function(response) {
// ...
});
And so on.
As a caveat, frontloading architectural decisions like these initially take a little development time away from features. However, as the codebase grows and develops, having simple, maintainable solutions like these are crucial to focusing on providing more value to clients (features, bug fixes, etc.) and smoothing out developer workflow.