Michael Herman

Software Developer

Kickstarting Angular With Gulp and Browserify, Part 2 - Browserify

Hello. Welcome to the second half. Last time, we built a nice Angular starter project, utilizing Gulp and Bower. Let’s take this a step further and add the power of Browserify into the mix. Before you read any further, check out the Introduction to the Browserify Handbook to learn about the problems that Browserify solves.

Just want the code? Get it here.

Install Dependencies

Let’s get Browserify installed…

First, install Browserify globally

1
$ npm install -g browserify

Then install the Gulp dependencies locally

1
$ npm install gulp-browserify gulp-concat --save

The former dependency allows you to run Browserify from Gulp, while the latter concatenates all the Bowerserify dependencies into a single JS file.

Update the Gulpfile

Update the requirements

1
2
var browserify = require('gulp-browserify');
var concat = require('gulp-concat');

Add the following tasks

1
2
3
4
5
6
7
8
9
gulp.task('browserify', function() {
  gulp.src(['app/js/main.js'])
  .pipe(browserify({
    insertGlobals: true,
    debug: true
  }))
  .pipe(concat('bundled.js'))
  .pipe(gulp.dest('./app/js'))
});

Now update the default task

1
2
3
4
// default task
gulp.task('default',
  ['lint', 'browserify', 'connect']
);

Update the HTML

Change the included JS file in index.html.

From:

1
<script src="./js/main.js"></script>

To:

1
<script src="./js/bundled.js"></script>

Test

To recap:

  1. We added Browserify
  2. Updated the build process so that a single JS file named bundled.js is created
  3. Updated index.html to include that new JS file
1
$ gulp

Navigate to http://localhost:8888/ and you should still see:

1
2
3
Angular-Gulp-Browserify-Starter

Testing...

Notice the bundled.js file. Again, this is generated by concat('bundled.js'). If you kill the server, then try to run it again, you’ll get an error. Essentially, the bundled.js file needs to be removed before each run. So update the clean task:

1
2
3
4
5
6
gulp.task('clean', function() {
    gulp.src('./dist/*')
      .pipe(clean({force: true}));
    gulp.src('./app/js/bundled.js')
      .pipe(clean({force: true}));
});

Browserify

Remember all those Bower components in the index.js file? Let’s clean up that mess by requiring our app’s dependencies with Browserify.

Update the HTML (again)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html ng-app="SampleApp">
  <head lang="en">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Angular-Gulp-Browserify-Starter</title>
    <!-- styles -->
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
    <link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="bower_components/animate.css/animate.css"/>
    <link rel="stylesheet" href="css/main.css"/>
  </head>
  <body>
    <div class="container">
      <h1>Angular-Gulp-Browserify-Starter</h1>
      <!-- views -->
      <div ng-view></div>
    </div>
    <!-- scripts -->
    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="./js/bundled.js"></script>
  </body>
</html>

Now, we need to use Browserify to require the following depencies in our app:

  1. angular.js
  2. angular-route.js
  3. angular-animate.js

Why don’t we replace all of our Bower components? It’s good to use both Bower and Browserify in case NPM does not have a certain dependency that Bower may have. The point of this example is to show you how to use both.

Install Requirements

Go ahead and install the requirements we need via NPM:

1
$ npm install angular angular-route angular-animate --save

Update JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(function () {

  'use strict';

  require('angular');
  require('angular-route');
  require('angular-animate');

  angular.module('SampleApp', ['ngRoute', 'ngAnimate'])

  .config([
    '$locationProvider',
    '$routeProvider',
    function($locationProvider, $routeProvider) {
      $locationProvider.hashPrefix('!');
      // routes
      $routeProvider
        .when("/", {
          templateUrl: "./partials/partial1.html",
          controller: "MainController"
        })
        .otherwise({
           redirectTo: '/'
        });
    }
  ]);

  //Load controller
  angular.module('SampleApp')

  .controller('MainController', [
    '$scope',
    function($scope) {
      $scope.test = "Testing...";
    }
  ]);

}());

Now we can include various modules the “Node-way” using require() calls, giving you access to nearly 90,000 modules.

Controller

Let’s abstract out the controller to a file of its own.

First, update main.js again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(function () {

  'use strict';

  require('angular');
  require('angular-route');
  require('angular-animate');
  var mainCtrl = require('./controllers/mainctrl');

  angular.module('SampleApp', ['ngRoute', 'ngAnimate'])

  .config([
    '$locationProvider',
    '$routeProvider',
    function($locationProvider, $routeProvider) {
      $locationProvider.hashPrefix('!');
      // routes
      $routeProvider
        .when("/", {
          templateUrl: "./partials/partial1.html",
          controller: "MainController"
        })
        .otherwise({
           redirectTo: '/'
        });
    }
  ])

  //Load controller
  .controller('MainController', ['$scope', mainCtrl]);

}());

Now create a new folder called “controllers” within “app/js”. In the new folder add a new file called mainctrl.js:

1
2
3
4
module.exports = function($scope) {
  $scope.test = "Testing...";
  console.log("required!");
};

This syntax should look familiar if you’ve worked with Node before. We use exports to expose the function, which we then have access to in main.js since it’s part of the requirements.

Test Again

1
2
$ gulp clean
$ gulp

Navigate to http://localhost:8888/ to make sure everything still works.

Update the Build

Now that we have the default task working, let’s update the build process so we can create a deployable build.

Update the Gulpfile

Add the following task to the gulpfile:

1
2
3
4
5
6
7
8
9
gulp.task('browserifyDist', function() {
  gulp.src(['app/js/main.js'])
  .pipe(browserify({
    insertGlobals: true,
    debug: true
  }))
  .pipe(concat('bundled.js'))
  .pipe(gulp.dest('./dist/js'))
});

This task simply updates where the bundled.js is stored after creation.

Finally, update the build task itself adding in the above task:

1
2
3
4
5
6
7
// build task
gulp.task('build', function() {
  runSequence(
    ['clean'],
    ['lint', 'minify-css', 'browserifyDist', 'copy-html-files', 'copy-bower-components', 'connectDist']
  );
});

Create a Build

1
$ gulp build

Check out the live app at http://localhost:9999/. Deploy your app, if you’d like.

Conclusion

Let’s recap. Over the past two posts, we’ve created a sample app that can be used as a seed for all of your Angular projects. Want to use this in your own projects?

  1. Clone the repo
  2. Install the global requirements: npm install -g gulp bower browserify
  3. Install the local requirements: npm install
  4. Install the Bower components: bower install
  5. Run locally: gulp
  6. Create a build: gulp build

I encourage you to add your favorite libraries and modules, which is easy to do. Looking for a client side dependency? Be sure to check NPM first before relying on Bower so you can take advantage of the simple require calls, via Browserify, which reduces code clutter and enables you to write modular, re-usable code.

As always, I’d love to hear your feedback. How are you using Browserify in your projects? Comment below.

Thanks for reading.

Comments