How (and Why) to Use browserify-rails to Include Angular 1.x in a Rails Project

Datetime:2016-08-23 02:34:38          Topic: AngularJS  Ruby on Rails           Share

100% of the Angular/Rails projects I’ve seen have had serious problems.

Probably the #1 problem I see is that the Angular code is too tangled up with the Ruby code. If for no other reason, this is bad because it makes the code confusing as hell to look at. Just look at this quick example:

<divng-show="#{foo}">
  {{ bar[#{baz}] }}
</div>

I tried to make that example extra bad on purpose but it’s really not too far from some real instances I’ve seen. In fact, the real code I’ve seen has often been way worse.

One way for the Ruby and JavaScript to get too intertwined is in the code itself. Another way is how you include it.

JavaScript-in-a-gem

There’s something I’ve heard called the “JavaScript-in-a-gem” pattern that you’ve probably seen. Somebody creates a wrapper for some JavaScript library in the form of a gem. And you’ve probably had at least a vague sense that this is kind of dumb. What’s wrong with just including the JavaScript library directly?

There’s a gem called angularjs-rails which is apparently pretty popular. Perhaps people google “angularjs rails” and that’s what they get. As far as I can tell, the gem is pretty much pointless because all it does is grab some Angular files for you.

Other inclusion options

Another way of including Angular in a Rails project, a pretty straightforward one, is to go download the official Angular JavaScript file and include it in the asset pipeline. This is less bad than JavaScript-in-a-gem but it’s still not my favorite. It feels very old-fashioned to have to navigate to a JavaScript library’s website, click the download link, then move the downloaded file into your Rails project. We don’t manage Ruby libraries in such a manual way. Why should we handle JavaScript libraries that way?

What I want is to be able to run a single command to install any JavaScript library I want.

Modern JavaScript package managers

Fortunately, this is totally possible. Bower and NPM both offer this capability and either can be integrated with Rails.

It seems that these days the internet prefers NPM over Bower, so I’d rather integrate NPM with Rails than Bower.

In order to get NPM packages to be usable on the front-end, you need something like Browserify .

There exists a browserify-rails gem which allows us to easily integrate Browserify, and therefore NPM, with Rails. (Yes, this is a JavaScript-in-a-gem library. But it’s the JavaScript-in-a-gem library that allows us to escape JavaScript-in-a-gem, and it’s the one and only JavaScript-in-a-gem library we’ll be using.)

Here’s how to use it.

Installing browserify-rails

First, add gem 'browserify-rails' to your Gemfile and do a bundle install .

$ bundleinstall

According to the browserify-rails documentation, we need to create a package.json that includes the browserify and browserify-incremental packages.

{
  "name": "something",
  "dependencies" : {
    "browserify": "~10.2.4",
    "browserify-incremental": "^3.0.1"
  },
  "license": "MIT",
  "engines": {
    "node": ">= 0.10"
  }
}

Now install the packages. At the end you’ll end up with a node_modules directory at your project root.

$ npminstall

Now that that worked, we’ll install Angular.

$ npminstall --saveangular

This will add Angular to our package.json and give us a node_modules/angular directory.

Creating a page where we can put some Angular code

What we’ll do next is to create a page where we can put a little JavaScript. Because I want to show this example in JavaScript as opposed to CoffeeScript, we’ll need to change our JavaScript engine to JavaScript from the default CoffeeScript. We can do this by modifying config/application.rb as follows.

require_relative 'boot'
 
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
 
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
 
module Hello
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
 
    config.generators do |g|
      g.javascript_engine :js
    end
  end
end

Now let’s generate a HomeController with a single action, index .

rails g controllerhomeindex

We’ll set the root route to home#index .

Rails.application.routes.draw do
  get 'home/index'
 
  root 'home#index'
end

Here’s the part where we actually add Angular. Remove everything from app/assets/javascripts/application.js and replace it with this:

require('angular')
 
angular.module('HelloApp', [])
  .controller('HomeController', function HomeController($scope) {
    $scope.foo = 'bar';
  })

Now replace app/views/home/index.html.erb with this content:

<divng-app="HelloApp">
  <divng-controller="HomeController">
    {{ foo }}
  </div>
</div>

If you refresh the page and see “bar”, it worked.





About List