npm Blog (Archive)

The npm blog has been discontinued.

Updates from the npm team are now published on the GitHub Blog and the GitHub Changelog.

Using Angular’s new improved Browserify support

With the recent release 1.3.14 instantaneous-browserification, Angular introduced better support for those using Browserify. Thank you to everyone who worked on that issue, and especially to Ben Clinkinbeard for his unflagging dedication in getting it in.

In this article, I’ll show you how Browserify can make your Angular apps better.

The problem

Let’s say that you want to use angular-material in your application… maybe you want to use its <md-autocomplete> element.

We’ve created an example that does just that.

pic of material

What scripts do you have to include? Well, you need to include angular and angular-material. You also need to know that angular-material uses both angular-animate and angular-aria, so you have to include those too.


<script src="./node_modules/angular-animate/angular.js"></script>
<script src="./node_modules/angular-animate/angular-animate.js"></script>
<script src="./node_modules/angular-aria/angular-aria.js"></script>
<script src="./node_modules/angular-material/angular-material.js"></script>

You’re only using one library, but you have to know about four different scripts and the paths to those scripts.

diagram using require

It would be great if your app only needed to know about angular-material, and if angular-material knew about its own dependencies… and that’s one of the things Browserify helps you do.

diagram using require

To do this, we’re going to need to make changes both to angular-material and to our app.

Changes in a module like angular-material

If you were the maintainer of a module like angular-material, what changes would you need to make for this to work?

  1. Declare dependencies in package.json

    You’ll need to specify the version of the other module. You should specify this as a range, rather than an exact version of the other module. For example, you could use 1.x to specify anything in the 1 major range. Or you could use ^1.3.0 to specify anything higher than 1.3.0 in the 1 major range.

    "dependencies": {
      "angular": "^1.3.0",
      "angular-animate": "^1.3.0",
      "angular-aria": "^1.3.0"
    }
    
  2. Add an index.js

    Browserify will look for the module’s main file. You can either name it index.js, or you can add a different name for it in the main field of package.json.

    This file should use the require function to let Browserify know which other modules need to be loaded. This should just be the name of the module, as you listed it in package.json. Browserify will figure out what the path to that module’s main file is.

    You’ll also want to make sure Browserify loads your module’s code. For this, you will need to use the relative path. For example, require('./angular-material.js').

    Finally, to match the convention for CommonJS set up within Angular, you’ll need to expose the string that people use to load your module. You’ll do this using module.exports. Note, this is a convention that Angular uses, and it doesn’t necessarily apply to other front-end projects.

    require('angular-animate');
    require('angular-aria');
    require('./angular-material');
    module.exports = 'ngMaterial';

Changes in the app

Now that angular-material plays well with Browserify, we can take advantage of that in our app. Check out the demo to see it all in action.

  1. Install browserify globally

    Browserify is a command line tool that you use when building your project. It isn’t loaded when you’re running your app. This means it should be installed globally, not as a dependency of the project.

    npm install -g browserify

    If you get an error that says EACCES, check out our docs on [fixing permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions), or just run the command with sudo.

  2. Add angular-material as a dependency

    You need a package.json file for your application to do this. If you don’t have one, use npm init to create one.

    Then you can just run:

    npm install --save angular-material

    Note: In the example repo, you’ll see that we use linclark/bower-material here instead. That’s because the changes to the module that we made in the last section haven’t been merged in and published to npm yet, so we’re depending on our fork instead.

  3. Create your script

    Create a file named entry.js to hold your script. The important thing to do in your script is to use require. For example, for the demo, we start with the following code:

    var angular = require('angular');
    
    angular
      .module('autocompleteDemo', [require('angular-material')])
      .controller('DemoCtrl', DemoCtrl);
    
    

    You can see the whole script in the demo.

    require is used twice here. It is used to get the angular variable, and then it is used again to get the name of the module that the injector will use. Using require in this way makes the dependencies explicit so that Browserify knows that it needs to go fetch the scripts for angular, angular-material, and any of their dependencies—angular-aria and angular-animation in this case.

  4. Bundle up the scripts

    Now we run Browserify to bundle all the scripts together into a single file, bundle.js.

    browserify entry.js -o bundle.js
    
  5. Use bundle.js in index.html

    Now that all of the dependencies are in bundle.js, we can remove the script tags for angular, angular-aria, angular-animate, and angular-material and replace them with a single script tag for bundle.js.

    <script src="bundle.js"></script>

To wrap it up

You should be able to depend on something without having to know about its dependencies, and about the dependencies of its dependencies. With this change, angular-material declares its own dependencies, making it easier for you to use it throughout your app.

So next time you are using an Angular module and have to include a bunch of script tags, think about putting in a PR and making things a little easier to bundle up.