Fork me on GitHub

What every Angular project likely needs – and a Gulp build to provide it

Reading time ~11 minutes

| Follow @CodepediaOrg

What every Angular project likely needs – and a Gulp build to provide it


Codever Logo

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repos on Github ā­šŸ™


Starting an AngularJs project also means choosing a whole toolchain that goes along with Angular. In this blog post we will propose a set of tools that a new Angular project will likely need, and present a simple Gulp build that provides those features. Let’s go over the following topics:

* Why Gulp? Goals for a baseline Angular build * Why use a CSS pre-processor, and why Sass * The advantages of a module system, and why browserify * Pre-populating the Angular template cache * Angular-friendly Javascript minification * Reducing the number of HTTP requests using sprites * Setup a development web server * Running tests with Karma, code quality with JSHint, and more ### Why choose Gulp? {#whychoosegulp} Although there are other task runners out there, it seems that Gulp is more and more the solution of choice, because it provides the following features: * it’s super fast as it’s build for concurrency from the start * it provides a simple to learn API (based on a pipes and filters architecture) that produces some very readable and maintainable code. ### Goals of a good Angular build {#goalsofagoodangularbuild} Ideally, the build for an Angular project would: * be of low complexity, ideally not much more than 100 lines of code * be lightning fast, in the order of a few seconds * imply no dependencies to non-node tool chains * be exactly the same in both development and production * solve some Angular-specific issues: Angular-friendly Javascript minification and template cache pre-population * provide a development mode for developer productivity * allow to run tests and do code quality checks * minimize the number of HTTP requests needed in production, via CSS and Javascript bundling, as well as images sprites ### A working build and sample App {#aworkingbuildandsampleapp} In order to make it more concrete, here is the [build](https://github.com/jhades/angularjs-gulp-todo/blob/master/gulpfile.js) and applied it to a sample app: the TODO App from the [TODO MVC](https://todomvc.com/) project. You can try out the sample app [here](https://jhades.github.io/angularjs-gulp-todo/todo-app.html).

The build and sample app are also available in this Github repository.
In the next following sections we will go over a proposed toolchain for Angular projects, and walk through the Gulp build that provides those features.

### Why use a CSS pre-processor, and why Sass {#whyuseacsspreprocessorandwhysass} To quote the yearly Thoughworks [Technology radar](https://www.thoughtworks.com/radar/languages-and-frameworks/handwritten-css) from last year: > We believe that the days of handwritten CSS, for anything apart from trivial work, are over.

The limitations of plain CSS are plenty and make it hard to write maintainable stylesheets. Although new CSS3 standards are addressing many of the limitations, widespread adoption takes time.

#### CSS enhanced features {#cssenhancedfeatures} In order to produce maintainable CSS, we really need today: * A CSS bundling mechanism * A partials mechanism, that allows to split CSS in logical chunks, without generating additional http requests * the possibility to define CSS variables of limited scope * the possibility to embedded related styles in a tree structure * the ability to define CSS ‘functions’

All these features are provided by the multiple CSS pre-processors available today, the difficulty being how to choose one. The two main ones are Sass and Less.

Many libraries are built on top of these pre-processors that encapsulate commonly used patterns, see for example the Sass [Compass](https://compass-style.org/) library. #### Sass or Less? {#sassorless} The Less pre-processor would seem to be a better choice for a node-based build, as its also a node-based tool. But today Sass is not tied to the Ruby tool-chain anymore, as there is a C implementation of Sass ([libsass](https://libsass.org/)) that allows for fast Sass compilation in a node environment, via plugins like [gulp-sass](https://github.com/dlmanning/gulp-sass). Also, the industry seems to be converging around Sass: see for example this post on [css-tricks](https://css-tricks.com/sass-vs-less/). In the end, the three main reasons that I decided to opt for Sass where: * good integration with node via libsass, no need to install Ruby * knowing that the most used Sass library Compass is being ported to libsass for Compass 2, see this [Github issue](https://github.com/Compass/compass/issues/1916) * the fact that Angular Material Design is based on [Sass](https://github.com/angular/material/tree/master/src/core/style), so we are likely to find it again in the future ### The build-css task {#thebuildcsstask} Sass is integrated in the gulp build via the `build-css` task bellow:
gulp.task('build-css', ['clean'], function() {
    return gulp.src('./styles/*')
        .pipe(sourcemaps.init())
        .pipe(sass())
        .pipe(cachebust.resources())
        .pipe(sourcemaps.write('./maps'))
        .pipe(gulp.dest('./dist'));
});
There is support for source maps, so it will be possible to debug the CSS in the browser and have it refer to the original Sass sources. More on the cache busting feature later. ### Why use a module system {#whyuseamodulesystem} There are several reasons why a module system is likely essential for any non-trivial Javascript project: * Adding all the Javascript dependencies in script tags at the bottom of the multiple html pages quickly falls apart as the codebase and the team increases * and what about concatenation and minification? repeating the content of the script tags in concatenation tasks at the level of the build quickly becomes unmanageable Besides solving those common problems out of the box, using a module system encourages: * the development of well isolated small modules with clearly defined boundaries and dependencies * structuring the code in small chunks that are on its own easy to understand * simplify and better enable testing

But the industry is very fragmented in what concerns module systems: there is requireJs, CommonJs, browserify, ES6 modules to name but a few. So which one to choose?

### Why CommonJs is appealing {#whycommonjsisappealing} CommonJs is the same module system used by node in general. It is very simple to understand and use. If we need a module, say for exampleĀ [express](https://expressjs.com/), we simply say:
var express = require('express');
Importing a module is synchronous and very intuitive. The main advantage of this syntax is that there is no need to define a config file where all the dependencies are declared: we only need to point to the entry file, and the `require` calls will themselves implicitly document the dependency tree of Javascript files. CommonJs used to be a backend-only module system, until [browserify](https://browserify.org/)Ā came along, allowing us to use the same familiar syntax also on the front-end. ### Angular now officially supports browserify {#angularnowofficiallysupportsbrowserify}

Recently in the Angular release 1.3.14 instantaneous-browserification the Angular team added improved support for browserify.

Namely it published in npm all Angular files as CommonJs modules, giving them meaningful exports that allow for simplified browserify usage.

Although ES6 modules will be the solution of choice for Angular 2, it seems that browserify is the recommended solution for Angular 1 projects.

### The build-js Gulp task {#thebuildjsgulptask} Going back to our build, the following is the task that builds the Javascript bundle of our application:
gulp.task('build-js', ['clean'], function() {
    var b = browserify({
        entries: './js/app.js',
        debug: true,
        paths: ['./js/controllers', './js/services', './js/directives'],
        transform: [ngAnnotate]
    });

    return b.bundle()
        .pipe(source('bundle.js'))
        .pipe(buffer())
        .pipe(cachebust.resources())
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(uglify())
        .on('error', gutil.log)
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest('./dist/js/'));
});
#### Why not use the gulp-browserify plugin {#whynotusethegulpbrowserifyplugin}

Note that browserify is used directly and not via some plugin like gulp-browserify. This is because the gulp team has blacklisted several plugins such as gulp-browserify, as they make for separate build processes that are hard to integrate well with other gulp plugins.

Instead the gulp team provided a recipe of how to use browserify: we simply call it directly, and pipe its output which includes the concatenated js files wrapped in modules and stream it to other gulp tasks.

The Gulp recipe was adapted to fix one Angular-specific problem: minification support. ### Angular-friendly Javascript minification {#angularfriendlyjavascriptminification} Angular is well-known for needing an [extra build step](https://stackoverflow.com/questions/18782324/angularjs-minify-best-practice) before minification in order to support its dependency injection mechanism. Take for example this code line:
angular.factory('todoStorage', function ($http, ) { ...
If we don’t take any preventive measure, this will get minified to something like:
a.factory('todoStorage', function (h, ) { ...

The problem is that the variable name $http is lost, so Angular can no longer inject it by finding the $httpProvider that it identifies by concatenating the variable name $http with Provider. This causes the application to break if minified.

Before minification, the above line needs to be rewritten to an alternative array syntax that preserves the string names but is a bit more verbose:
angular.factory('todoStorage', ['$http', function ($http, ) { ... } ]
#### Fixing the Angular minification problem {#fixingtheangularminificationproblem} One way to fix it is to simply use the array syntax everywhere, as this is still readable and avoids an extra build step. Alternatively, the browserify transform `browserify-annotate` was included in the `build-js` task above to solve this:
var ngAnnotate = require('browserify-ngannotate');
Note that this transform applies the array syntax in almost all places, but I still had to apply it manually to route definition `resolve` steps, see the file `app.js`. ### Pre-populating the Angular template cache {#prepopulatingtheangulartemplatecache}

Another Angular-specific problem that a build needs to solve is to pre-populate the Angular template cache.

If we don’t do this, then every template of every directive will result in a separate HTTP request, resulting in an unacceptably slow application startup time, as the application needs to wait for the templates to be loaded before rendering the page.

### Reducing the number of HTTP requests using sprites {#reducingthenumberofhttprequestsusingsprites} Many of the optimizations that the build provides revolve around reducing the number of HTTP requests needed to bootstrap the app. And what about images, such as for example icons?

One way to reduce the number of HTTP requests related to images is to group them together in an image sprite, and load only that. The following section of the build uses the gulp.spritesmith plugin to generate sprites:

gulp.task('sprite', function () {

    var spriteData = gulp.src('./images/*.png')
        .pipe(spritesmith({
            imgName: 'todo-sprite.png',
            cssName: '_todo-sprite.scss',
            algorithm: 'top-down',
            padding: 5
        }));

    spriteData.css.pipe(gulp.dest('./dist'));
    spriteData.img.pipe(gulp.dest('./dist'))
});

All the png images inside the images folder get converted into one single todo-sprite.png file. In order to be able to use the sprites, a Sass mixin is generated in file _todo-sprite.scss. The mixin can be used in the following way:

@include sprites($spritesheet-sprites);

This will create a set of CSS classes with the same names as the image files, which allow to use the different images. For example the imageĀ cal-right-button.jpg could be used in the following way:

<span class="cal-right-button"></span>
### Why use cache busting? {#whyusecachebusting}

A convenient practice that comes in handy in both development and production is to prevent the browser from keeping stale copies of theĀ CSS / Javascript of the application.

This prevents customers from viewing a stale version of the application that no longer matches the Html, and avoids bug reports that are sometimes hard to link back to the presence of stale resources.

The most effective way to achieve cache busting is to append to each CSS/Javascript file a hash of its content. This way when the file changes, the file name also changes and the browser will load the latest version of the resource.

### Implementing cache busting with gulp-cachebust {#implementingcachebustingwithgulpcachebust} The plugin [gulp-cachebust](https://www.npmjs.com/package/gulp-cachebust) was used to implement the cache busting functionality. We have seen in the `build-css` and `build-js` tasks a call like this:
.pipe(cachebust.resources())

This call keeps track of the CSS and Javascript files that will need to have their names concatenated with the file hash. The actual replacement of the css file names is done in the build task, by callingĀ cachebust.references():

gulp.task('build', [ 'clean', 'bower','build-css','build-template-cache', 'jshint', 'build-js'], function() {
    return gulp.src('index.html')
        .pipe(cachebust.references())
        .pipe(gulp.dest('dist'));
});
### Running tests with Karma {#runningtestswithkarma}

One of the de-facto Angular test tool is the Karma test runner, and one the most used testing frameworks with it is Jasmine. The following Gulp task allows to run Jasmine tests from the command line against a headless PhantomJs browser:

gulp.task('test', ['build-js'], function() {
    var testFiles = [
        './test/unit/*.js'
    ];

    return gulp.src(testFiles)
        .pipe(karma({
            configFile: 'karma.conf.js',
            action: 'run'
        }))
        .on('error', function(err) {
            console.log('karma tests failed: ' + err);
            throw err;
        });
});
### Code quality with JsHint {#codequalitywithjshint}

JsHint is one of the most used Javascript linters. The following gulp task allows to integrate it in our build cycle:

gulp.task('jshint', function() {
    gulp.src('/js/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
});
### A development web server {#adevelopmentwebserver} After doing the main build and running all the tests using the `gulpĀ `default task, its useful to be able to startup a local development server. The `gulp-webserver` plugin was configured to do exactly that in the following way:
gulp.task('webserver', ['watch','build'], function() {
    gulp.src('.')
        .pipe(webserver({
            livereload: false,
            directoryListing: true,
            open: "<a href="http://localhost:8000/dist/index.html">http://localhost:8000/dist/index.html</a>"
        }));
});
### Conclusions {#conclusions}

With the Gulp task runner and its rich ecosystem of plugins, its possible to create a baseline Angular build with the most frequently used features that an Angular project will likelly need, and still keep the complexity of the build relativelly low.

Let us know your thoughts on the comments bellow, what does your Angular build look like?

Published at Codepedia.org with permission of Aleksey Novik ā€“ sourceĀ What every Angular project likely needs – and a Gulp build to provide itĀ from https://blog.jhades.org/

Subscribe to our newsletter for more code resources and news

routerLink with query params in Angular html template

routerLink with query params in Angular html template code snippet Continue reading

Top