AngularJS  structure with Gulp, Node and Browserify

AngularJS structure with Gulp, Node and Browserify

  • 2016-08-25
  • 2318

We will see how we can structure an AngularJS app. There are quite a few setups that work well for smaller apps. Here we will look at an AngularJS setup using Gulp, Browserify and Node that is more scalable and manageable when our app expands and becomes larger.
This article also provides an example project and also a quickstart scalable setup for setting up your next AngularJS project.

Tools

Node/npm

The tools used below require node. Use npm as package manager instead of bower, as it works better with browserify to require files into the application.

Gulp

Automate your workflow using Gulp. Gulp is a task runner and there is a gulp-plugin for almost any task that can be automated.

Browserify

If you have worked on a node project you would have came across node modules and how node conveniently allows us to use require to import them into our application.

Browsers do not have a built-in require functionality and if our app is large then we will end up loading lots script tags into our application. Browserify lets us use require in our client application. It reads the required dependencies and creates a single browser compatible file which can be included into the application.

BrowserSync

BrowserSync is a tool that lets us do synchronized browser testing. It will create a server and will simultaneously run our application in multiple browsers. It works well with Gulp. We can watch for changes in our code using gulp watch and ask browsersync to reload all browsers.

Directory Structure

There are quite a few ways to structure an angular app. AngularJS encourages modularization. Separating files based on modules or components makes your structure more scalable and manageable. Let’s see it with an example.

node_modules/
src/
    app/
        components/ (Smaller mini apps/modules)
            home/
                home.html
                home.js (mainApp.home, controller and or services)
            signup/
                signup.js
                signup.html
        shared/ (Shared across two or more components/directives)
            sidebar/
                sidebar.html
                sidebarDirective.js
        app.js (Main app/configurations and routes.)
    assets/
        css/
        scss/
        js/
    env/ (Can be single or multiple files depending on the setup)
        prod.js
        dev.js
    index.html
public/
gulpfile.js
package.json

src

Should contain the source code.

app

All the angular app code goes here.

app/app.js

Your main Angular app. Here you will require other mini apps and angular libraries and inject them into your main app. Your angular app configuration and run block can be here. You can add routes into this file although if your files starts to get too big then its better to separate routes into specific component files.

components

For the app to be scalable, it is necessary to keep it modular. components directory will contain the mini independent angular apps or modules, which combine together into a single larger app.

shared

Here the code snippets or parts of the application which would be shared across the app or components should be placed. It could be a shared directive or template or filters.

assets

Assets folder would contain all your app assets that are not related to AngularJS code. For eg. stylesheets, fonts etc.

env

Should contain environment setting files. Gulp can be used to include the appropriate file into the app based on the environment (prod/qa) of execution.

public

Build folder where gulp will output the final files.

Getting Started

Install Node

Download and install node from here.

Initial Setup

Create a project folder. Navigate into it using command line. Then run npm init. This will ask a few question and create a package.json file. This file is used for managing dependencies for node.

Installing Gulp

Install gulp globally

npm install gulp -g

Also remember to save it as a developer dependency.

npm install gulp --save-dev

Working with browserify

Lets see with an example how we can use browserify.

Install Browserify

npm install --save-dev browserify

Install vinyl-source-stream, it’s a package which takes the browserify stream and converts it into something that gulp understands.

npm install vinyl-source-stream --save-dev

Once both are installed, require them in your gulpfile.js file.
Now lets see how we can require script files/ libraries (eg: angular.js) into our client application. Install angularjs using npm. Note to require files using browserify you should use npm as your package manager or else you will have to specify full path to require the files.

npm install angular --save

Now in your app.js require angular require(‘angular’). You don’t have to include it in your html.
Next lets write a browserify task that will take our app.js file and convert it into some thing that the browser will understand.

  var browserify = require('browserify');
    var source = require('vinyl-source-stream');

    gulp.task('browserify', function() {
        // Grabs the app.js file
        return browserify('./src/app/app.js')
            // bundles it and creates a file called main.js
            .bundle()
            .pipe(source('main.js'))
            .pipe(gulp.dest('./public/'));
    });

Above the browserify task takes app.js bundles it into main.js and puts it in the public directory which is the distribution folder. Now we need to include this main.js file into our index.html file.

require('angular');
var app = angular.module('myApp', []);
    <html>
        <head>
            <title>Home</title>
        </head>
        <body ng-app="myApp">
        </body>
        <script type="text/javascript" src="main.js"></script>
    </html>

Working with browser-sync

Install browser-sync

npm install browser-sync -–save-dev

Configure gulp task for browser-sync.

//./gulpfile.js
   var browserSync = require('browser-sync').create();
    gulp.task('browser-sync', function() {
        browserSync.init({
            server: {
                baseDir: "./public",
                // The key is the url to match
                // The value is which folder to serve (relative to your current working directory)
                routes: {
                    "/bower_components": "bower_components",
                    "/node_modules": "node_modules"
                }
            },
            browser:"firefox"
        });
    });

This will create a server and serve files to the browser from the public directory. Check my previous article to know more about BrowserSync.

Environment specific builds

You may want to have diffent builds for different environment (eg: dev, prod). You may choose not to minify and concat files in development enviroment and only do it on production. Use gulp-environments for environment specific builds.

npm install gulp-environments -–save-dev

Lets say we have different configuration files for dev and prod and we want to include them in our application respectively.

./gulpfile.js

var environments = require('gulp-environments');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var development = environments.development;
var production = environments.production;
/** load config file based on enviroment */
var configFile = production() ? "./src/env/prod.js" : "./src/env/dev.js";
gulp.task('scripts', function(){
    return gulp.src(['./src/assets/**/*.js',configFile])
            .pipe(uglify())
            .pipe(concat('vendor.min.js'))
            .pipe(gulp.dest('./public/'));
});

Now to run build for specific environment run the below commands.

gulp scripts --env production

The above command builds for production. By default production and development are the two available environments although we can add more. Have a look at the documentation for more options.

Install other required gulp plugins

npm install gulp-jshint -–save-dev
npm install gulp-sass -–save-dev
npm install gulp-uglify -–save-dev
npm install gulp-concat -–save-dev

Demo Application

Let’s have a look at a demo application to get a real view of the above setup.

/gulpfile.js</

    var gulp = require('gulp');
    var sass = require('gulp-sass');
    var browserify = require('browserify');
    var source = require('vinyl-source-stream');
    var buffer = require('vinyl-buffer');
    var jshint = require('gulp-jshint');
    var browserSync = require('browser-sync').create();
    var uglify = require('gulp-uglify');
    var concat = require('gulp-concat');
    var environments = require('gulp-environments');

    var development = environments.development;
    var production = environments.production;
    /** load config file based on enviroment */
    var configFile = production() ? "./src/env/prod.js" : "./src/env/dev.js";

    gulp.task('lint', function() {
      return gulp.src('./src/app/**/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
    });

    gulp.task('scripts', function(){
        return gulp.src(['./src/assets/**/*.js',configFile])
                .pipe(uglify())
                .pipe(concat('vendor.min.js'))
                .pipe(gulp.dest('./public/'));
    });

    gulp.task('browserify', function() {
        // Grabs the app.js file
        return browserify('./src/app/app.js')
            // bundles it and creates a file called main.js
            .bundle()
            .pipe(source('main.js'))
            .pipe(gulp.dest('./public/'));
    })

    gulp.task('copy', ['browserify','scss'], function() {
        gulp.src(['./src/**/*.html','./src/**/*.css'])
            .pipe(gulp.dest('./public'))
            .pipe(browserSync.stream())
    });

    gulp.task('scss', function() {
        gulp.src('./src/assets/scss/*.scss')
            .pipe(sass().on('error', sass.logError))
            .pipe(gulp.dest('./src/assets/stylesheets/'));
    });

    gulp.task('build',['lint', 'scss', 'copy', 'scripts']);

    gulp.task('browser-sync', ['build'], function() {
        browserSync.init({
            server: {
                baseDir: "./public",
                // The key is the url to match
                // The value is which folder to serve (relative to your current working directory)
                routes: {
                    "/bower_components": "bower_components",
                    "/node_modules": "node_modules"
                }
            },
            browser:"firefox"
        });
    });


    gulp.task('default', ['browser-sync'], function(){
        gulp.watch("./src/**/*.*", ["build"]);
        gulp.watch("./public/**/*.*").on('change', browserSync.reload);
    })

.src/app/app.js

require('angular');
require('angular-ui-router');
require('angular-aria');
require('angular-animate');
require('angular-material');
require('./components/home/home.js');
require('./components/about/about.js');

var app = angular.module('myApp', ['ui.router','ngMaterial','myApp.home','myApp.about'])

app.config(function($stateProvider, $urlRouterProvider) {
   
    $urlRouterProvider.otherwise("/");
   
    $stateProvider
    .state('home', {
        url: "/",
        views : {
            "" : {
                templateUrl:"app/components/home/home.html"
            },
            "[email protected]":{
                templateUrl:"app/shared/header/header.html"
            }
        }
    })
    .state('about', {
        url: "/about",
        views : {
            "" : {
                templateUrl:"app/components/about/about.html"
            },
            "[email protected]":{
                templateUrl:"app/shared/header/header.html"
            }
        }
    });
});

.src/index.html

<html>
    <head>
        <title>Home</title>
        <link rel="stylesheet" href="node_modules/angular-material/angular-material.min.css">
        <link rel="stylesheet" href="assets/stylesheets/style.css">
    </head>
    <body ng-app="myApp">
        <div ui-view></div>
    </body>
    <script type="text/javascript" src="vendor.min.js"></script>
    <script type="text/javascript" src="main.js"></script>
</html>

.package.json

{
  "name": "angularapp",
  "version": "1.0.0",
  "description": "A scalable structure for angular apps",
  "main": "\u001b ",
  "scripts": {
    "test": " "
  },
  "repository": {
    "type": "git",
    "url": " "
  },
  "author": "Rahil Shaikh",
  "license": "MIT",
  "devDependencies": {
    "browser-sync": "^2.9.11",
    "browserify": "^12.0.0",
    "gulp": "^3.9.0",
    "gulp-concat": "^2.6.0",
    "gulp-environments": "^0.1.1",
    "gulp-jshint": "^1.11.2",
    "gulp-minify": "0.0.5",
    "gulp-sass": "^2.1.0",
    "gulp-uglify": "^1.4.2",
    "vinyl-buffer": "^1.0.0",
    "vinyl-source-stream": "^1.1.0"
  },
  "dependencies": {
    "angular": "^1.4.7",
    "angular-animate": "^1.4.7",
    "angular-aria": "^1.4.7",
    "angular-material": "^0.11.4",
    "angular-ui-router": "^0.2.15"
  }
}

Other then these there are component files and stylesheet which you can find with the downloaded code.

Quick Start

If you want to quick start your next AngularJS project with the above structure you can download the code from the link.
Alternatively you can clone the repository by running
git clone https://github.com/rahil471/scalable-angularjs-project-setup-gulp-browserify.git

Next naviagte to the project directory from your command-line and run

npm install

This will install all the necessary node modules. Next we also need to install gulp globally.

npm install -g gulp

Now run

gulp

Your application will run in firefox browser (as configured in browser-sync), you can change the browser or add another browser by editing the browser-sync task in gulpfile.js

Bonus

In this section I’ve listed some of the most popular and useful gulp plugins other than the one’s we used above.

gulp-util

Provides utility functions. Useful for logging and coloring outputs etc. Know more.

gulp-autoprefixer

Parses css and adds vendor(browser) prefixes to your css. Know more.

gulp-load-plugins

Loads all your gulp plugins at once, hence you won’t have to require each plugin one at a time. Know more.

Conclusion

Angular encourages modularaization, to make your app scalabale and managable you should divide it into smaller independent components and bring these smaller components to complete a larger app. With tools like gulp and browserify, this becomes much easier as you won’t have to include every single component file into your index.html, instead just one js file. Also tools like BrowserSync helps us do browser testing with ease there by saving a lot of our time. I hope this tutorial will make your life with AngularJS much more easier.

DOWNLOAD

Suggest

Visual Studio 2015: Building JavaScript Apps with AngularJs

AngularJS 2.0: What to Expect and Interesting?

JavaScript Promises: Applications in ES6 and AngularJS

Learn Angular 2 Development By Building 10 Apps

Angular 2 and NodeJS - The Practical Guide to MEAN Stack 2.0