• US Inquiries / 1 877 517 6540
  • Canadian Inquiries / 1 866 206 4644
submit to reddit

August 28, 2015 / KB-005

Building AngularJS Apps with Grunt.js (Part 2)

Check out these related webinars...

Unit Testing AngularJS Code
Get Ready for Angular 2.0
Introduction to AngularUI and UI-Bootstrap
Building AngularJS Apps with Grunt.js
Hybrid Mobile Development with AngularJS and Ionic
JavaScript for Web 2.0 Development
AngularJS

A Build System for AngularJS applications

In part 1 of this article, we used Yeoman to setup a folder for a new AngularJS project. Now, let's take a look at the project that we setup.

Grunt.js

'Grunt.js', also known simply as 'grunt' is a task runner. Those of you coming from a Java background are probably familiar with Apache Ant. Grunt is sort of like Apache Ant, but written in and for JavaScript.

We installed the command line tool previously ('sudo npm install -g grunt-cli'), so the 'grunt' command is available in our local binary folder (this is probably '/usr/local/bin', but it may vary on your system).

When we run 'grunt', it reads a configuration file, 'Gruntfile.js' to find the tasks that are defined. If we have a look at the first part of, we can notice a few things...

//... A few lines deleted...
module.exports = function (grunt) {

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Automatically load required Grunt tasks
  require('jit-grunt')(grunt, {
    useminPrepare: 'grunt-usemin',
    ngtemplates: 'grunt-angular-templates',
    cdnify: 'grunt-google-cdn'
  });

  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist'
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: appConfig,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

  //... More follows...

First, notice that although it's a configuration file, it's actually a JavaScript source file that defines a function. This function is the exported object for a module in the 'CommonJS' module style. To actually do the initialization, 'Grunt' is going to call this function, passing in a reference to 'Grunt' itself.

This function does two basic things -

  1. It calls 'grunt.initConfig()' to store configuration information for all the plugins and tasks that are used in the build.
  2. It defines the tasks and the dependencies between them. We can call these tasks from the command line, or they can be used as dependencies (or pre-requisites) for other tasks. This part is the 'registerTask(...)' calls.

Tasks

When we generated the template project with Yeoman's Angular generator, it defined a few tasks for us:

  • serve: This task does a development build, starts a local web server, and then launches a browser on our product, complete with "live reload". Also, this task sets up 'Grunt' to watch files for changes, and then trigger a reload on the browser when we save a file. We'll take a closer look at this task in few minutes.
  • test: Runs unit testing with the Karma framework.
  • build: Runs a complete build on the distributable package - We'll take a look at this one later as well.
  • default: Runs a JavaScript syntax checker, runs the tests, and then runs the build task.

Let's check out a few of these tasks...

grunt serve

In the first part of this article, we created an application called 'great-app'. Let's go back to that, and return to the folder that we created.

$ cd great-app/

Now, let's startup our application...

$ grunt serve

What should have happened is that you see a bunch of output, and then a browser window magically opened with a sample application running in it.

Web Browser with Default App

Right now it doesn't look very impressive, but we'll fix that shortly. First, though, notice that when we ran 'grunt serve', it didn't actually return to the shell prompt. What's going on?

'Grunt' is still sitting there, looking at the files that are mentioned under the 'watch' configuration. When we edit the files, 'grunt' will rebuild the system and then reload the browser window for us. Let's give that a try.

Open an editor on 'app/views/main.html'. Find the section that reads:

<h4>HTML5 Boilerplate</h4>
<p>
  HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.
</p>

...and delete it, then save the file. Notice how the web browser updates itself automatically to reflect the updated view?

Bower and Bootstrap

Now let's spice up the appearance a little bit. The "template" app is intended to use the Bootstrap framework (http://getbootstrap.com), but when we answered 'yo's questions, we said no to Bootstrap. Let's add that in after the fact.

To do this, we'll use the 'Bower' package manager. Bower is usually used to take care of client-side packages like Bootstrap. We'll add in the precompiled version of Bootstrap's css files using Bower.

Leave the 'grunt serve' task running. Make sure that you don't have any unsaved files in your editor. Open up another terminal window in the same folder, and then type:

$ bower install --save bootstrap

You should see that there's now a 'bootstrap' folder under bower_components. The '--save' option tells Bower to update the 'bower.json' file, which tracks what packages we depend on.

Just above, we noted that the web browser updates itself automatically when we make changes to the view. Given the completeness of this setup, you might expect the build system to figure out that we added Boostrap, and then update the view automatically, but the view didn't appear to change.

In reality, it did change, ever-so-slightly. If you open up the 'app/index.html' file in your editor, and look for the comment that starts with '', you should be able to find the following lines:

<!-- bower:js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
<!-- endbower -->

Notice that there's a callout to 'bower_components/bootstrap/dist/js/bootstrap.js'. What's happened is that the 'Gruntfile.js' specifies a tool called 'wiredep' (short for 'wire up the dependencies'). When we installed Bootstrap using Bower, Bower changed the file 'bower.json', which lists all the packages maintained by Bower. 'Grunt watch' picked up the change and ran the 'wiredep' task in response. 'wiredep' read the 'bower.json' file and then edited the file 'index.html' to include Boostrap's entry point for JavaScript, 'bootstrap.js'.

But what about Bootstrap's cascadeable style sheet? Well, if you look again in 'index.html', you'll find another 'bower:css' block in the head element, but it looks like the Bootstrap CSS file wasn't picked up.

The reason is complex, and at the current time, a little contentious (Explanation here - NSFW language). Basically, Bootstrap is composed of a group of stylesheets that are compiled with the 'less' stylesheet compiler. The distribution includes the compiled stylesheet as well, but the new specification for Bower dictates that 'bower.json' should only call out source files, not compiled files. The correct way to handle things would be to setup a 'less' task in Grunt and have it compile Bootstrap when building. That would allow us to modify Bootstrap as it was intended. However, we would still be left with some questions about how and when to compile the stylesheets, and how to deal with updates to Bootstrap. That's a bigger discussion than we want to have in this article, so we'll cheat a little bit, since we're not actually going to customize Bootstrap.

Copy the file 'bootstrap.css' from 'bower_components/bootstrap/dist/css' to 'app/styles'.

Now, open the file 'app/index.html', and find the comment line that starts with

<!-- build:css(.tmp) styles/main.css -->

Edit the block so it looks like:

<!-- build:css(.tmp) styles/main.css -->
<link rel="stylesheet" href="styles/main.css">
<link rel="stylesheet" href="styles/bootstrap.css">
<!-- endbuild -->

Now, when you save the file, you should see that the web browser updates itself with a more pleasant-looking page, as a result of using the Bootstrap package.

Sample page with Bootstrap

Summary

That's it for now! You now have a build system setup with Grunt.js! You can add packages with Bower. You've seen how we can update the view page and have our browser update automatically using 'grunt serve'.

In a future Knowledge Base article, we'll create some AngularJS components and explore unit testing, and then we'll produce a 'production' build of our system using Grunt.

Related Webinars

Related Training

WA2425
AngularJS Training
AngularJS Programming
Toronto
Vancouver
Instructor Led Virtual
Calgary
Ottawa
WA2406
AngularJS Training
Introduction to Responsive Web Development with AngularJS and Bootstrap
Ottawa
Toronto
Calgary
Vancouver