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
// build task
gulp.task('build',
  ['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.

Kickstarting Angular With Gulp and Browserify, Part 1 - Gulp and Bower

Let’s develop an Angular boilerplate. Why? Despite the plethora of Angular seeds/generators/templates/boilerplates/starters/etc. on Github, none of them will ever do exactly what you want unless you build your own, piece by piece. By designing your own, you will better understand each component as well as how each fits into the greater project. Stop fighting against a boilerplate that just doesn’t fit your needs and start from scratch. Keep it simple, as you learn the process.

In this first part, we’ll start with Angular and Gulp, getting a working project setup. Next time we’ll add Browserify into the mix.

This tutorial assumes you have Node.js installed and have working knowledge of NPM and Angular. Just want the code? Get it here.

Project Setup

Install Dependencies

Setup a project folder and create a package.json file:

1
2
$ mkdir project_name && cd project_name
$ npm init

The npm init command helps you create your project’s base configuration through an interactive prompt. Be sure to update the ‘entry point’ to ‘gulpfile.js’. You can just accept the defaults on the remaining prompts.

Do the same for Bower:

1
$ bower init

Accept all the defaults. After the file is created update the ignore list:

1
2
3
4
5
6
7
"ignore": [
  "**/.*",
  "node_modules",
  "app/bower_components",
  "test",
  "tests"
],

Install global dependencies:

1
$ npm install -g gulp bower

Bower install directory

You can specify where you want the dependencies (commonly known as bower components) installed to by adding a .bowerrc file and adding the following code:

1
2
3
{
  "directory": "/app/bower_components"
}

Install local dependencies:

NPM

1
$ npm install gulp bower gulp-clean gulp-jshint gulp-uglify gulp-minify-css gulp-connect --save

Bower

1
$bower install angular angular-animate angular-route jquery animate.css bootstrap fontawesome --save

The --save flag adds the dependencies to the package.json and bower.json files, respectively.

We’ll address each of these dependencies shortly. For now, be sure you understand the project’s core dependencies:

  • Gulp is a Javascript task runner, used to automate repetitive tasks (i.e., minifying, linting, testing, building, compiling) to simplify the build process.
  • Bower manages front-end dependencies.

Folder Structure

Let’s setup a base folder structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
├── app
│   ├── bower_components
│   ├── css
│   │    └── main.css
│   ├── img
│   ├── index.html
│   ├── partials
│   │    ├── partial1.html
│   │    └── partial2.html
│   └── js
│   │    └── main.js
├── .bowerrc
├── .gitignore
├── bower.json
├── gulpfile.js
├── node_modules
└── package.json

Add the files and folders not already included. This structure is based on the popular Angular Seed boilerplate, developed by the Angular team.

Gulp

To start, we just need the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
// gulp
var gulp = require('gulp');

// plugins
var connect = require('gulp-connect');


gulp.task('connect', function () {
  connect.server({
    root: 'app/',
    port: 8888
  });
});

This allows us to serve our future Angular app on a development server running on port 8888.

Test

Let’s test it out. Add the word ‘hi’ to the index.html file, then run the following command:

1
$ gulp connect

Navigate to http://localhost:8888/ and you should see ‘hi’ staring back at you. Let’s build a quick sample app. Keep the server running…

Develop a Sample App

index.html

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
<!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/angular/angular.js"></script>
    <script src="bower_components/angular-route/angular-route.js"></script>
    <script src="bower_components/angular-animate/angular-animate.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="js/main.js"></script>
  </body>
</html>

This should look familiar. The ng-app directive initiates an Angular app while ng-view sets the stage for routing.

main.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
(function () {

'use strict';


  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...";
    }
  ]);

}());

Again, this should be relatively straightforward. We setup the basic Angular code to establish a route handler along with a controller that passes the variable test to the template.

partial1.html

Now let’s add the partial template:

1
<p>{{ test }}</p>

Test

Back in your browser, refresh the page. You should see the text:

1
2
3
Angular-Gulp-Browserify-Starter

Testing...

Create the Build

Now that our app is working locally, let’s modify our gulpfile.js to generate a deployable build. Kill the server.

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// gulp
var gulp = require('gulp');

// plugins
var connect = require('gulp-connect');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var minifyCSS = require('gulp-minify-css');
var clean = require('gulp-clean');

// tasks
gulp.task('lint', function() {
  gulp.src(['./app/**/*.js', '!./app/bower_components/**'])
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
    .pipe(jshint.reporter('fail'));
});
gulp.task('clean', function() {
    gulp.src('./dist/*')
      .pipe(clean({force: true}));
});
gulp.task('minify-css', function() {
  var opts = {comments:true,spare:true};
  gulp.src(['./app/**/*.css', '!./app/bower_components/**'])
    .pipe(minifyCSS(opts))
    .pipe(gulp.dest('./dist/'))
});
gulp.task('minify-js', function() {
  gulp.src(['./app/**/*.js', '!./app/bower_components/**'])
    .pipe(uglify({
      // inSourceMap:
      // outSourceMap: "app.js.map"
    }))
    .pipe(gulp.dest('./dist/'))
});
gulp.task('copy-bower-components', function () {
  gulp.src('./app/bower_components/**')
    .pipe(gulp.dest('dist/bower_components'));
});
gulp.task('copy-html-files', function () {
  gulp.src('./app/**/*.html')
    .pipe(gulp.dest('dist/'));
});
gulp.task('connect', function () {
  connect.server({
    root: 'app/',
    port: 8888
  });
});
gulp.task('connectDist', function () {
  connect.server({
    root: 'dist/',
    port: 9999
  });
});


// default task
gulp.task('default',
  ['lint', 'connect']
);
// build task
gulp.task('build',
  ['lint', 'minify-css', 'minify-js', 'copy-html-files', 'copy-bower-components', 'connectDist']
);

What’s happening here?

  1. gulp-jshint checks for code quality in the JS files. If there are any issues the build fails and all errors output to the console.
  2. gulp-clean removes the entire build folder so that we start fresh every time we generate a new build.
  3. gulp-uglify and gulp-minify-css minify JS and CSS, respectively.

Build commands

Default

The default task, gulp, is a compound task that runs both the lint and connect tasks. Again, this just serves the files in the “app” folder on http://localhost:8888/.

Build

The build task creates a new directory called “dist”, runs the linter, minifies the CSS and JS files, and copies all the HTML files and Bower Components. You can then see what the final build looks like on http://localhost:9999/ before deployment. You should also run the clean task before you generate a build.

Test this out:

1
$ gulp clean build

Conclusion

Well, hopefully you now have a better understanding of how Gulp can greatly simply the build process, handling a number of repetitive tasks. Next time we’ll clean up some of the mess that the Bower components leave behind by adding Browserify into the mix and detail a nice workflow that you can use for all your Angular projects.

Leave questions and comments below. Cheers!

Primer on AngularJS Services

What’s a service …

Much to my surprise, the Angular documentation provides a great definition of a service:

Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.

Angular services are:

  1. Lazily instantiated – Angular only instantiates a service when an application component depends on it.
  2. Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.

Angular offers several useful services (like $http), but for most applications you’ll also want to create your own.

Services are powerful in that they help keep your code DRY by encapsulating functionality. From an architecture standpoint alone, services help separate out concerns, ensuring that each object is responsible for a single piece of functionality. For example, it’s common for beginners to put all of their app’s functionality into the controller. This is fine for smaller apps, but just know that it’s not a good practice and your controller will balloon quickly as your app scales.

Get in the habit early on to separate concerns. If your controller is handling more than just defining the scope or initial state of your app, connecting your models and views, then it’s are probably doing too much.

We are all (err, I am) guilty of this. Let’s look at a very simple app …

HTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!doctype html>
<html lang="en" ng-app='myApp'>
<head>
  <meta charset="UTF-8">
  <title>Angular Boilerplate</title>
  <!-- styles -->
  <link href="http://netdna.bootstrapcdn.com/bootswatch/3.1.1/yeti/bootstrap.min.css" rel="stylesheet" media="screen">
  <link href="main.css" rel="stylesheet" media="screen">
</head>
  <body>
    <div class="container">
      <div ng-controller="myController">
        <h1>Enter Quantity:</h1>
        <input type="number" ng-model="quantity"></p>
        <h2>Total Cost: {{calculate(quantity) | currency}}</h2>
      </div>
    </div>
    <!-- scripts -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" type="text/javascript"></script>
    <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script src="main.js" type="text/javascript"></script>
  </body>
</html>

Javascript:

1
2
3
4
5
6
7
8
var app = angular.module('myApp', [])

app.controller('myController', function($scope) {
  $scope.quantity = 100;
  $scope.calculate = function(number) {
    return number * 10;
  }
});

You can grab the code from this repo or from JSFiddle.

So, this just takes an input value (integer or floating point) and multiplies it by 10 in the calculate() function, which then updates the DOM. Not only is the controller defining scope - but it also calculates the total. Despite this being a small app, too much is happening in the controller. We should separate out the calculate function() into a separate service.

Creating a custom service

By moving the business logic out of the controller, abstracting much of the code, our controller becomes leaner. It’s a good practice to write fat services and lean controllers.

To do this, we are will use a service type called a factory, which is the most common type.

This is a good time to stop and learn the major service types - constants, values, services, providers, and decorators. Check out this excellent article for more on the various service types and how and when to use them. All are slightly different, but, in general, all are dependency injected modules of functionality.

Within the same JS file add the following code beneath the controller:

1
2
3
4
5
6
7
8
// Service
app.factory('calculateService', function(){
  return {
    calculate: function(number){
      return number * 10
    }
 }
});

This code creates a service called calculateService. You may be wondering why we have to use the factory() method for this instead of just a regular function. It’s simple: That method registers the service with Angular; and with Angular aware of its existence, it can be dependency injected into the controller, giving us access to the defined functions - e.g, calculate() within the controller. We can now use this in multiple places within our application, allowing for easy code reuse.

So, we have simply abstracted the logic of taking the user inputted number and multiplying it by 10.

Now update the controller:

1
2
3
4
5
6
app.controller('myController', function($scope, calculateService) {
  $scope.quantity = 100;
   $scope.calculate = function(number) {
    return calculateService.calculate(number);
  }
});

And you’re app should be working. Test it out. JSFiddle

Conclusion

Hopefully, you now have a better sense as to -

  • What a service is,
  • Why you should use them, and
  • How to use them.

Want some practice? Create separate services for each piece of functionality in this app’s controller. Remember: The controller is responsible for defining scope, all else should be moved out of the controller altogether.

If you need help, start by creating a service that handles the actual API calls. Perhaps use a service name of getData then set up functions for the different HTTP requests - i.e., readData() for a GET request and writeData() for a POST. Then when you use dependency injection to add this service to your controller, you can simply use the following syntax for accessing the readData() function in the controller:

1
getData.readData(some_argument)

Presumably you would pass in an argument supplied by the user. Now you can access that function from the controller without knowing anything about the actual service except for how you use it. The controller is cleaner because you abstracted out all the messy code for making API calls.

Good luck!