Get data from your Rails app using Angular $resource

Dave Ceddia bio photo By Dave Ceddia Comment

Let’s imagine you’ve got a Rails app set up and running, and now you’re working on integrating AngularJS with it. Maybe you haven’t got much experience with Angular, but it seems popular and it can’t be that hard to figure out!

So just how do you go about retrieving the data from your Rails app? Surely Angular must have some way to just fetch some data from the server?

Two options: $http and $resource

Well you’d be right - Angular in fact has two ways to fetch data from a server, built-in services named $http and $resource. Here’s a handy table to decide which one to use:

  Use $http Use $resource
Data is a single file or URL X  
This is a one-off request that needs to be working quickly X  
Data is RESTful, with standard GET/POST/PUT/DELETE routes   X
The data represents a first-class “object” in your system   X
The data has many related routes that are more-or-less RESTful   X

From here on, I’m gonna go ahead and assume you’re using $resource to interact with the data – not because it’s always the best choice, but because I don’t want to make this article too long. I’ll cover $http in another post if there’s interest (let me know!).

Another contender: Restangular

I should mention that there’s another, 3rd party library called Restangular which serves as an alternative to Angular’s built-in $resource. I won’t be discussing it here, but if after reading this it seems like $resource doesn’t fit your needs, check out Restangular.

Using $resource to interact with your data

Let’s imagine that you’ve got a Rails controller that looks something like this (surprisingly like the default scaffold code):

class BrewersController < ApplicationController
  before_action :set_brewer, only: [:show, :edit, :update, :destroy]

  def index
    @brewers = Brewer.all
    render json: @brewers
  end

  def show
    render json: {brewer: @brewer, beers: @brewer.beers}
  end

  def create
    @brewer = Brewer.new(brewer_params)

    respond_to do |format|
      if @brewer.save
        format.html { redirect_to @brewer, notice: 'Brewer was successfully created.' }
        format.json { render :show, status: :created, location: @brewer }
      else
        format.html { render :new }
        format.json { render json: @brewer.errors, status: :unprocessable_entity }
      end
    end
  end

  # --- snip ---
end

Since this is modeling a Brewer, you can probably guess what our Angular $resource will be called. It will look something like this:

function BrewerFactory($resource) {
  var Brewer = $resource('/brewers/:id.json');

  // We could just return the $resource directly, but this way
  // allows us to add functionality through the Brewer's prototype:

  Brewer.prototype.countBeers = function() {
    return this.beers.length;
  }

  return Brewer;
}

angular.module('yourApp')
  .factory('Brewer', BrewerFactory);

Well hey, that’s not much code at all! The trick is that $resource, like Rails, gives you some functionality out of the box. You get these actions automatically:

  • query - calls GET /brewers.json and expects an array of objects in return
  • get - calls GET /brewers.json and expects a single object in return
  • save - calls POST /brewers/:id.json, setting the body of the POST to the data inside the resource, and using its id attribute in the URL
  • remove - calls DELETE /brewers/:id.json
  • delete - calls DELETE /brewers/:id.json

You can call them on the Brewer object itself, like so:

Brewer.query();
Brewer.get({id: 2});

And you can also call them on instances of Brewer:

brewer.$get();
brewer.$save();

Note the $ prefix, and that we don’t need to pass an id because the brewer has one internally.

One other important thing to note is that these methods return empty objects (or arrays, in query’s case) that will be filled in after the HTTP request completes. So just remember that if you try to do a console.log(Brewer.query()) you’ll get nothing. Instead, do var b = Brewer.query();, wait half a second, and then inspect the value of b – it’ll be filled in if all went well.

Let’s look at how you’ll use this new Brewer resource in your app. Suppose you have a simple list of brewers like this:

<ul ng-controller="BrewerCtrl as ctrl">
  <li ng-repeat="ctrl.brewer in brewers"> <a ng-click="ctrl.deleteBrewer(brewer)">X</a</li>
</ul>

And then in your controller, you need to fetch the list of brewers and make it available to the view. We’ll also implement the deleteBrewer function to get rid of the ones you don’t like.

function BrewerCtrl(Brewer) {
  var ctrl = this;

  // This is a little sneaky, see note below
  ctrl.brewers = Brewer.query();

  ctrl.deleteBrewer = function(brewer) {
    // 'brewer' is an instance of Resource here,
    // so you get all those actions mentioned above.
    // They're just prefixed with a $
    brewer.$delete();
  }
}

Remember that Brewer.query() immediately returns an empty array and then fills it in later? That’s what’s happening above, and when the request is complete, Angular will kick off a digest cycle, pick up the change, and re-render the list.

That’s basically it! Enough to get you started using Angular’s $resource.

There’s plenty more to it – you can add custom routes and actions, combine $resource with UI-Router to resolve resources and inject them into your controllers, and basically treat your resources as the client-side version of your Rails models. $resource is actually pretty powerful. But that’s enough for this post :)

If you want to get notified when I post other articles on topics like this, stuff your email into that box down there and hit ‘Subscribe’! You’ll get an average of under 1 email per week.

Choosing a framework is difficult.

Learning React? Sign up and get my React Learning Timeline PDF, and also weekly-ish articles about React, Angular, and JavaScript.

A few emails per month — unsubscribe any time.
comments powered by Disqus