Angular2: Step by Step guide of using Angular2 with Typescript for ASP.NET MVC in Visual Studio 2015

Standard

During last couple of months, I have received a lot of requests how to use angular2 in ASP.NET MVC in visual studio environment, Its quite easy to integrate angular2 in asp.net core project, so I am publishing step by step guide to integrate Angular 2 in ASP.NET MVC.

Pre Step:

    • Install Visual Studio

Download latest version of Visual Studio 2015 Community Edition with recent update (Update 3)

    • Install NodeJS and NPM

Download latest version of Nodejs and NPM (Make sure that you are running node version 4.4.x5.x.x, and npm 3.x.x by running node -v and npm -v in a terminal/console window. Older versions produce errors.)

    • Install Node.js Tools for Visual Studio (optional)

Download – Turn Visual Studio into a powerful Node.js development environment.

    • Install Typescript

Download latest version of Typescript (version >= 2.0)
OR
Ensure you have the latest version of Visual Studio 2015 installed. Then open Visual Studio and install the latest set of TypeScript tools as follows:

  • Open Tools | Extensions and Updates.
  • Select Online in the tree on the left.
  • Search for TypeScript using the search box in the upper right.
  • Select the most current available TypeScript version.
  • Download and install the package.
    • Install Package Installer extension (optional)

Download – Visual Studio extension that makes it easy and fast to install Bower, npm, JSPM, TSD, Typings and NuGet packages.


 Download Demo from Github

Step 1: Create an ASP.NET MVC project

Create the ASP.NET 4.x project as follows:

  • In Visual Studio, select File | New | Project from the menu.
  • In the template tree, select Templates | Visual C# (or Visual Basic) | Web.
  • Select the ASP.NET Web Application template, give the project a name, and click OK.
  • Select the desired ASP.NET 4.5.2 template (>= 4.x.x) and click OK.

screenshot_7
screenshot_8

  • Avoid Adding any authorization and authentication at this point of time to keep project quite simple

screenshot_9

Please note all configuration and package versions are according to Angular Quickstart Guide


Step 2: Create Package.json file

package.json identifies npm package dependencies for the project.

{
  "name": "ang2demo",
  "version": "1.0.0",
  "scripts": {},
  "license": "ISC",
  "dependencies": {
    "@angular/common": "~2.0.1",
    "@angular/compiler": "~2.0.1",
    "@angular/core": "~2.0.1",
    "@angular/forms": "~2.0.1",
    "@angular/http": "~2.0.1",
    "@angular/platform-browser": "~2.0.1",
    "@angular/platform-browser-dynamic": "~2.0.1",
    "@angular/router": "~3.0.1",
    "@angular/upgrade": "~2.0.1",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "5.0.0-beta.12",
    "systemjs": "0.19.39",
    "zone.js": "^0.6.25"
  },
  "devDependencies": {
    "@types/core-js": "^0.9.34",
    "typescript": "^2.0.3",
    "typings": "^1.4.0"
  }
}

screenshot_4

Note:

Please note that @types/core-js are not mentioned in Angular Quickstart Guide in devDepenedcies section, Add this to avoid duplicate identifier error otherwise you are going to get as “Angular 2 can’t find Promise, Map, Set and Iterator”


Step 3: Create tsconfig.json file

This file defines how the TypeScript compiler generates JavaScript from the project’s files.

For Visual Studio 2015 we must add "compileOnSave": true to the TypeScript configuration (tsconfig.json) file as shown here.

{
  "compileOnSave": true,
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false,
    "typeRoots": [
      "node_modules/@types"
    ],
    "types": [
      "core-js"
    ]
  }
}

screenshot_12

Note:

  • On creating this file you will receive alert from Visual Studio such as below, Just Press No:

screenshot_14

  • Please note that below code is not note mentioned in Angular Quickstart Guide in tsconfig.json, Add this to avoid duplicate identifier error otherwise you are going to get as “Angular 2 can’t find Promise, Map, Set and Iterator”

screenshot_2


Step 4: Create typings.json file

This file provides additional definition files for libraries that the TypeScript compiler doesn’t natively recognize.

{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160725163759",
    "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
    "node": "registry:dt/node#6.0.0+20160909174046"
  }
}

screenshot_3


Step 5: Install package.json file

Open CMD and redirect to your application folder and Using npm from the command line, install the packages listed in package.json with the command:

> npm install

screenshot_19

After executing command, output will be like this.

screenshot_20

Note:

    • Error messages—in red—might appear during the install, and you might see npm WARN messages. As long as there are no npm ERR! messages at the end, you can assume success.
    • Do not include the node_modules folder in the project. Let it be a hidden project folder.But you may view the hidden folder in Visual Studio using “Show All Files” option in Solution Explorer.

screenshot_22


Step 6: Create Sample Angular 2 Code using Typescript

I am using sample code from Angular Quickstart Guide

1. Create a folder name “App” in Scripts folder

2. Create application module file

Angular itself is split into separate Angular Modules. This makes it possible for you to keep payload size small by only importing the parts of Angular that your application needs.Every Angular application has at least one module: the root module, named AppModule here.

Create the file App/app.module.ts with the following content:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }   from './app.component';
@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

This is the entry point to your application.

Root module needs to import the BrowserModule from @angular/platform-browser to the imports array.

This is the smallest amount of Angular that is needed for a minimal application to run in the browser.

3. Create a component & add it to your application

Every Angular application has at least one component: the root component, named AppComponent here.Components are the basic building blocks of Angular applications. A component controls a portion of the screen—a view—through its associated template.

Create the file App/app.component.ts with the following content:

import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: 'My First Angular App - Demo' }) 
export class AppComponent { }

4. Create a Start up file

Now we need to tell Angular to start up your application.

Create the file App/main.ts with the following content:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

This code initializes the platform that your application runs in, then uses the platform to bootstrap your AppModule.

Note:

  • Please note that transplied typescript files will automatically be available in App folder as we have mentioned attribute CompileOnSave is true in tsconfig.json

Files visible in Visual Studio:

screenshot_2

Files visible in Folder:

screenshot_3


Step 7: Create systemjs.config.js file

This file provides information to a module loader about where to find application modules, and registers all the necessary packages.

Create the file Scripts/systemjs.config.js with the following content:

/**
 * System configuration for Angular samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
    System.config({
        paths: {
            // paths serve as alias
            'npm:': '/node_modules/'
        },
        // map tells the System loader where to look for things
        map: {
            // our app is within the app folder
            app: '/Scripts',
            // angular bundles
            '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
            '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
            '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
            '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
            '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
            '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
            '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
            '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
            // other libraries
            'rxjs': 'npm:rxjs'
        },
        // packages tells the System loader how to load when no filename and/or no extension
        packages: {
            app: {
                main: './main.js',
                defaultExtension: 'js'
            },
            rxjs: {
                defaultExtension: 'js'
            }
        }
    });
})(this);

screenshot_5

Note:

    • npm attribute highlighted in red color in above image should point to folder which has all installed packages, in our case it is node_modules folder.
    • app attribute highlighted in red color in above image should point to folder which has all application transcript code, in our case it is Scripts folder.
    • main attribute highlighted in red color in above image should point to js file which contains angular application startup code, in our case it is main.ts file.

Step 8: Load and Render Angular2 application in ASP.NET MVC Views

screenshot_15

  • In order to load angular 2 application in MVC, integrate angular 2 libraries references and system.js configurations in Views/Shared/_Layout.cshtml file
   
   
   @ViewBag.Title - My ASP.NET Application
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    http://~/node_modules/core-js/client/shim.min.js
    http://~/node_modules/zone.js/dist/zone.js
    http://~/node_modules/reflect-metadata/Reflect.js
    http://~/node_modules/systemjs/dist/system.src.js

    
    http://~/Scripts/systemjs.config.js
    
        System.import('../../Scripts/App/main').catch(function (err)
        {
            console.error(err);
        });
    

 System.import('../../Scripts/App/main')
  • In order to kickstart angular code in browser, integrate component in Views/Home/index.cshtml file
@{
    ViewBag.Title = "Home Page";
}

Loading...

Step 9: Build and run the app

Click the Run button or press CTRL + F5 to build and run the application.

This launches the default browser and runs the QuickStart sample application.

screenshot_2

Try editing any of the project files. Save and refresh the browser to see the changes.


In case of Error such as:

  • Error 1

Compiler errors such as “Property map does not exist on type Observable” and “Observable cannot be found” indicate an old release of Visual Studio. Exit Visual Studio and follow the instructions here.

You’ll be asked to replace the file

c:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TypeScript.typescriptServices.js

This operation requires admin privileges.

OR Stackoverflow link

  • Error 2

IDE issues such as ‘component can not be properly resolved, probably its located in an inaccessible module’

screenshot_3

occurs when angular2 keyowrd are highlighted red as no intellisense is available for them by Visual Studio 2015 visible in below images

screenshot_4 screenshot_6

Inorder to resolve them make sure you have Resharper -> Check your Resharper Typescript Language settings. Resharper might be set to older version of typescript 1.6.  Download latest version of resharper and restart Visual Studio. In case it doesnt work try to set Typescript language level to 2.0 under Inspection Tab from Resharper Options menu. Stackoverflow


Cheers. Happy Coding.

AngularJS: Textarea Auto Resize Directive

Standard

Custom directive that resize text area as per user key input or through paste/cut

Over the years I have seen many solutions for this problem, so I have written angualr directive to resolve this issue. I have prefer vertical resize because horizontal resize strikes me as being a mess, due to word-wrap, long lines, and so on, but vertical resize seems to be pretty safe and nice. Please note instead of clientHeight attribute use scrollHeight

Features

  • On key input.
  • With pasted text (right click & ctrl+v).
  • With cut text (right click & ctrl+x).
  • With pre-loaded text.
  • With all textarea’s (multiline textbox’s) site wide.
  • Is w3c validated.

Github Link

Angular Code:

(function () {
  'use strict';

  angular
      .module('sampleApp' , [])
      .directive('autoResize', autoResize);

      autoResize.$inject = ['$timeout'];

      function autoResize($timeout) {

          var directive = {
              restrict: 'A',
              link: function autoResizeLink(scope, element, attributes, controller) {

                  element.css({ 'height': 'auto', 'overflow-y': 'hidden' });
                  $timeout(function () {
                      element.css('height', element[0].scrollHeight + 'px');
                  }, 100);

                  element.on('input', function () {
                      element.css({ 'height': 'auto', 'overflow-y': 'hidden' });
                      element.css('height', element[0].scrollHeight + 'px');

                  });
              }
          };

          return directive;
      };
})();

Angular HTML Code:

<textarea auto-resize style="resize: none;"></textarea>

Initial State:

capture png

After user input through keystrokes:

capture

After user input through paste:

capturez png

AngularJS: Dynamic Validation Directive

Standard

Custom directive that allows text only, number only, special character only or allow everything as per user choice.

Github Link

Angular Code:

(function () {
    'use strict';

    angular
        .module('sampleApp')
        .directive('validate', validate);

    function validate() {
        var directive = {
            restrict: 'EA',
            scope: {
                regex: '=criteria'
            },
            require: 'ngModel',
            controller: validateController,
            link: validateLink
        };

        return directive;
    }

    validateController.$inject = ['$scope'];

    function validateController($scope) {
    }

    function validateLink(scope, element, attrs, ngModelCtrl) {

        element.on('focus', function (e) {
            ngModelCtrl.$parsers.push(validateInput);
        });

        function validateInput(input) {
            if (input) {
                var regex = new RegExp(scope.regex);
                var newValue = input.replace(regex, '');
                if (newValue !== input) {
                    ngModelCtrl.$setViewValue(newValue);
                    ngModelCtrl.$render();
                }
                return newValue;
            }
            return undefined;
        }
    }

})();

Output:

2016-07-01 14_07_02-mozilla firefox

AngularJS : Tree Component

Standard

Tree Control is an AngularJS component that cad add siblings, child and remove them. It doesn’t depend on jQuery.

I have tried number of tree control but none is available out in open source community which can assist you to add siblings too. Since I want to control number of childs that can be added to single node. So I came up with my own tree control implementation.

Features

  • Data binding for the tree model
  • Data binding for the selected node in the tree
  • Adding siblings
  • Customize number of childs to be added on a single node(Default: 6)
  • Remove node
  • Recusive controller and Html code
  • Can be customized with CSS

Github Link

Initial State:

2016-07-06 14_28_36-index html

After adding multiple siblings and children:

2016-07-06 14_34_26-index html

Alert if you remove parent with children:

2016-07-06 14_34_43-index html

Alert if number of childs exceeds:

2016-07-06 14_38_26-index html

 

Angular Code:

(function () {
    'use strict';
    
    var app = angular
        .module('sampleApp', []);
    
    app.controller('HomeController', homeController);
 
    homeController.$inject = [];
 
    function homeController() {
        var vm = this;
 
        init();
 
        function init() {
            vm.detailList = [];
            vm.ID = 0;
            vm.parentRecord = {};
            addRootNode();
            vm.AddSibling = AddSibling;
            vm.AddChild = AddChild;
            vm.Delete = Delete;
        }
 
        ////////////////////////////Implementation//////////////////////////////////////
 
        function AddSibling(item) {
            if (item.parentId == 0) {
                addRootNode();
            }
            else {
                findParentRecord(vm.detailList, item.parentId);
                if (vm.parentRecord) {
                    AddChild(vm.parentRecord);
                }
            }
        }
 
        function AddChild(item) {
            var depth = findNodeLevel(vm.detailList, item.ID);
            if (depth < 7) {
                vm.ID = vm.ID + 1;
                item.nodes.push({
                    nodes: [],
                    parentId: item.ID,
                    ID: vm.ID
                });
            }
            else
                alert('maximum level has been reached');
        }
 
        function Delete(item) {
            if (item.nodes.length > 0)
                alert('delete children first');
            else {
                DeleteChild(vm.detailList, item.parentId, item);
            }
        }
 
        ////////////////////////////Helping Function//////////////////////////////////////
 
        function addRootNode() {
            vm.ID = vm.ID + 1;
            vm.detailList.push({
                nodes: [],
                parentId: 0,
                ID: vm.ID
            });
        }
 
        function findParentRecord(arr, parentId) {
            for (var i in arr) {
                if (typeof (arr[i]) == "object") {
                    if (arr[i].ID == parentId) {
                        vm.parentRecord = arr[i];
                        break;
                    }
                    else
                        findParentRecord(arr[i], parentId);
                }
            }
        }
 
        function DeleteChild(arr, parentId, item) {
            for (var i in arr) {
                if (typeof (arr[i]) == "object") {
                    if (arr[i].ID == parentId) {
                        var childrenList = arr[i].nodes;
                        var index = findIndex(childrenList, item.ID);
                        if (index > -1)
                            childrenList.splice(index, 1);
                        break;
                    }
                    else
                        DeleteChild(arr[i], parentId, item);
                }
            }
        }
 
        function findIndex(arr, selectedItemID) {
            for (var i in arr) {
                if (arr[i].ID == selectedItemID)
                    return i;
            }
            return -1;
        }
 
        function findNodeLevel(arr, id) {
            var level = 1;
            findParentRecord(arr, id)
            while (vm.parentRecord.parentId > 0) {
                level = level + 1;
                findParentRecord(arr, vm.parentRecord.parentId);
            }
            return level;
        }
    };
})();

 

Angular HTML Code:

<html>
    <body ng-app="sampleApp" class="ng-cloak">
        
                             
                                                                                                                                               
                            
  •                     
                
            </script>             
                
                
                    
                            
  •                     
                
            </div>             

Automation Features of GulpJS

Standard

 

  1. JS Code Style Guide and Analysis
  • Use this task in order to implement your javascript style guide and detect errors.
  • Highlight all rules in your .jshintrc and .jscsrc file.
  • Gulp will test javascript code from these files in order to enforce rules.

Sample Js Hint File

Sample Js Code Style File

Pre Install:

    npm install --save-dev gulp gulp-load-plugins 

Install:

    npm install --save-dev gulp-load-plugins jshint-stylish gulp-util

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });

    gulp.task('jsCode', function () {
    return gulp
        .src('test/*.js')
        .pipe($.jscs())
        .pipe($.jshint())
        .pipe($.jshint.reporter('fail'));
    });

Execute:

    gulp jscode

 

 

2. less and sass compilation

  • Use gulp-less in order to compile less into css before browsing
  • Task will take care of variables, operators, functions, mixins etc in your less file
  • You may use AutoPrefixer to add vendor prefixes

Pre Install:

    npm install --save-dev gulp gulp-load-plugins 

Install:

    npm install --save-dev gulp-less gulp-autoprefixer 

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });

    gulp.task('css', function () {
    return gulp
        .src('test/*.less')
        .pipe($.less())
        .pipe($.autoprefixer({ browsers: ['last 2 version', '> 5%'] }))
        .pipe(gulp.dest('./build/'));
    });

Execute:

    gulp css

 

 

3. Add Javascript and CSS dynamically in main file

  • Use wiredep to inject bower js and css dependencies into index.html
  • Use gulp-inject to inject custom js and css dependencies into index.html

Pre Install:

    npm install --save-dev gulp gulp-load-plugins

Install:

    npm install --save-dev wiredep gulp-inject

Syntax in HTML File for wiredep:

    <!-- bower:css -->

    <!-- endbower -->

    <!-- bower:js -->

    <!-- endbower -->

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });

    var options = {
        bowerJson: require('./bower.json'),
        directory: './bower_components/',
        ignorePath:  '../..'
    }

    gulp.task('injectJS', function () {
        var wiredep = require('wiredep').stream;

        return gulp
            .src('./src/index.html')
            .pipe(wiredep(options))
            .pipe($.inject(gulp.src('modules/home/**/*.js')))
            .pipe(gulp.dest('./build/'));
    });

Execute:

    gulp injectJS

Syntax in HTML File for gulp-inject:

    <!-- inject:css -->

    <!-- endinject -->

    <!-- inject:js -->

    <!-- endinject -->  

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });

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

        return gulp
            .src('./src/index.html')
            .pipe($.inject(gulp.src('content/**/*.css')))
            .pipe(gulp.dest('./build/content/'));
    });

Execute:

    gulp injectCSS

 

 

4. Automatic Browser Refresh

  • Use BrowserSync to view live changes in html/JS/CSS to browser
  • Proxy attribute should have IIS path and port
  • Ghost Mode synchronize actions across browsers e.g. scroll, click etc

Browser Sync

Pre Install:

    npm install --save-dev gulp lodash node-notifier

Install:

    npm install --save-dev browser-sync

Code:

    var gulp = require('gulp');
    var browserSync = require('browser-sync');
    var _ = require('lodash');

    gulp.task('browser-sync', function () {
        if (browserSync.active) {
            return;
        }
        var options = {
            proxy: 'localhost:' + 3472,
            port: 3470,
            files: [
                './scripts/**/*.*'
            ],
            ghostMode: {
                clicks: true,
                location: false,
                forms: true,
                scroll: true
            },
            injectChanges: true,
            logFileChanges: true,
            logLevel: 'debug',
            logPrefix: 'gulp-patterns',
            notify: true,
            reloadDelay: 1000 
        };
        browserSync(options);
    });

    function notify(options) {
        var notifier = require('node-notifier');
        var notifyOptions = {
            sound: 'Bottle',
            contentImage: path.join(__dirname, 'gulp.png'),
            icon: path.join(__dirname, 'gulp.png')
        };
        _.assign(notifyOptions, options);
        notifier.notify(notifyOptions);
    }

Execute:

    gulp browser-sync   

 

 

5. Compressing Images

  • Use gulp-imagemin to minify PNG, JPEG, GIF and SVG images
  • Use optimizationLevel attribute to adjust compression between 0 and 7

Pre Install:

    npm install --save-dev gulp gulp-load-plugins 

Install:

    npm install --save-dev gulp-imagemin 

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });

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

        return gulp
            .src('./src/images/')
            .pipe($.imagemin({ optimizationLevel: 4 }))
            .pipe(gulp.dest('./build/images'));
    });

Execute:

    gulp images

 

 

6. Copy Files

  • For copying files like fonts, non-compressing images etc

Code:

    var gulp = require('gulp');

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

        return gulp
            .src('./src/fonts/')
            .pipe(gulp.dest('./build/fonts'));
    });

Execute:

    gulp delete

 

 

7. Delete Files

  • For deleting folders and files.

Install:

    npm install --save-dev del

Code:

    var gulp = require('gulp');
    var del = require('del');

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

        del('./build/');
    });

Execute:

    gulp delete

 

 

8. List all task Files

  • Use gulp-task-listing in order to list all tasks in your gulpjs file

Pre Install:

    npm install --save-dev gulp gulp-load-plugins 

Install:

    npm install --save-dev gulp-task-listing 

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });

    gulp.task('help', $.taskListing);

Execute:

    gulp help

 

 

9. Caching HTML Templates

  • Use gulp-angular-templatecache in order to concatenates and registers AngularJS templates in the $templateCache
  • All HTML files will be stored as key-value pair using angular $templateCache service
  • URL of HTML will be key and html code of file will be value in $templateCache service
  • This will reduce HTTP requests
  • For each HTML request by angular, first it will check $templateCache service, If not found then it will make HTTP request for that HTML file
  • standAlone attribute in options means to make a new angular module for templates
  • gulp-angular-templatecache will create a file and using .run() service of main module, this will load all html in $templateCache service

Pre Install:

    npm install --save-dev gulp gulp-load-plugins 

Install:

    npm install --save-dev gulp-angular-templatecache gulp-minify-html 

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });
    var options =   {
                module: 'ModuleName',
                standAlone: false,
                root: 'scripts/app/'
            }   

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

        return gulp
            .src('**/*.html')
            .pipe($.minifyHtml({ empty: true }))
            .pipe($.angularTemplatecache(
                    'templates.js',
                    options
                ))
            .pipe(gulp.dest('./.tmp/'));
    });

Execute:

    gulp templatecache

 

 

10. Combing all JS and CSS into respective one file

  • Use gulp-useref in order to concatenate all css into one css file and all js files into one js file
  • Parse build blocks in HTML files to replace references to non-optimized scripts or stylesheets.
  • Similar to gulp-inject
  • Index.html should have bower/inject/build section, bower and inject area should be populated first using wiredep and gulp-inject task, then execute this task to concatenate all css and js file into respective single file as mentioned under build tag of index.html

Pre Install:

    npm install --save-dev gulp gulp-load-plugins

Install:

    npm install --save-dev gulp-useref@v1.1.0

Syntax in HTML File for gulp-useref:

    <!-- build:css content/lib.css -->
    <!-- bower:css -->
    <!-- endbower -->
    <!-- endbuild -->

    <!-- build:css content/site.css -->
    <!-- inject:css -->
    <!-- inject -->
    <!-- endbuild -->

    <!-- build:js js/lib.js -->
    <!-- bower:js -->
    <!-- endbower -->
    <!-- endbuild -->

    <!-- build:js js/app.js -->
    <!-- inject:js -->
    <!-- endinject -->
    <!-- endbuild -->

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });
    var assets = $.useref.assets({ searchPath: './' });

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

        return gulp
            .src('./build/index.html')
            .pipe(assets)
            .pipe(assets.restore())
            .pipe($.useref())
            .pipe(gulp.dest('./build/'));
    });

Execute:

    gulp combine
  • $.useref.assets() collects assets from the HTML comment
  • $.useref.restore() restore the files to the index.html
  • $.useref() will concatenate files

 

 

11. Minifying Files

  • Use gulp-Uglify to remove whitespace, comments, minify javascript files
  • Use gulp-CSSO to remove whitespace, comments, transform css files
  • Use gulp-filter to seperate out/reduce/filter files from gulp stream
  • Manglin your code can break angular dependency injection, in order to avoid this use Strict Dependency Injection (ng-strict-di) or $inject Property Annotation
  • Filter.restore() function put filter back into stream

Pre Install:

    npm install --save-dev gulp gulp-load-plugins 

Install:

    npm install --save-dev gulp-filter@v2.0.0 gulp-csso gulp-uglify

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });
    var assets = $.useref.assets({ searchPath:  './' });
    var cssFilter = $.filter('**/*.css');
    var jsFilter = $.filter('**/*.js');

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

        return gulp
            .src('./build/index.html')
            .pipe(assets)
            .pipe(cssFilter)
            .pipe($.csso())
            .pipe(cssFilter.restore())
            .pipe(jsFilter)
            .pipe($.uglify())
            .pipe(jsFilter.restore())
            .pipe(assets.restore())
            .pipe($.useref())
            .pipe(gulp.dest('./build/'));
    });

Execute:

    gulp minify 

 

 

12. Angular Dependency Injection

  • Use gulp-ng-annotate in order to provide automatic security blanket because manglin your code can break angular dependency injection
  • gulp-ng-annotate looks for dependency injection and adds angular injection
  • Use ** /* @ngInject / * annotation on top of controller, services, anonymous function etc to resolve dependency injection if forget to use Strict Dependency Injection (ng-strict-di) or $inject Property Annotation.
  • @ngInject provide code hint for gulp-ng-annotate to add angular injections
  • Avoid implementing @ngInject for third party libraries

Pre Install:

    npm install --save-dev gulp gulp-load-plugins

Install:

    npm install --save-dev gulp-ng-annotate

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });
    var assets = $.useref.assets({ searchPath:  './' });
    var cssFilter = $.filter('**/*.css');
    var jsLibFilter = $.filter('**/' + config.optimized.lib);
    var jsAppFilter = $.filter('**/' + config.optimized.app);

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

        return gulp
        .src('./build/index.html')
        .pipe(assets)
        .pipe(cssFilter)
        .pipe($.csso())
        .pipe(cssFilter.restore())
        .pipe(jsLibFilter)
        .pipe($.uglify())
        .pipe(jsLibFilter.restore())
        .pipe(jsAppFilter)
        .pipe($.ngAnnotate())
        .pipe($.uglify())
        .pipe(jsAppFilter.restore())
        .pipe(assets.restore())
        .pipe($.useref())
        .pipe(gulp.dest('./build/'));
    });

Execute:

    gulp di

 

 

13. Revisions

  • Use gulp-rev in order to implement static asset revisioning by appending content hash to filenames: unicorn.css => unicorn-d41d8cd98f.css
  • Use gulp-rev-replace in order to rewrite occurences of filenames which have been renamed by gulp-rev in index.html(html referencing file)
  • gulp-rev-replace solves the cache problem too

Pre Install:

    npm install --save-dev gulp gulp-load-plugins 

Install:

    npm install --save-dev gulp-rev gulp-rev-replace

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });

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

        return gulp
            .src('src/*.js')
            .pipe($.rev())
            .pipe(gulp.dest('./build/'));
    });

Execute:

    gulp revision

 

 

14. File Version

  • Use gulp-bump for vesrioning in package.json and bower.json
  • –type=pre will bump the prerelease version ..*-x
  • –type=patch or no flag will bump the patch version ..x
  • –type=minor will bump the minor version .x.
  • –type=major will bump the major version x..
  • –version=1.2.3 will bump to a specific version and ignore other flags
  • for –version=1.2.3 means 1 corresponds to major, 3 corresponds to minor and 3 corresponds to package version

Pre Install:

    npm install --save-dev gulp gulp-load-plugins yargs

Install:

    npm install --save-dev gulp-bump

Code:

    var gulp = require('gulp');
    var $ = require('gulp-load-plugins')({ lazy: true });
    var args = require('yargs').argv;

    gulp.task('version', function () {
        var type = args.type;
        var version = args.version;
        var options = {};
        if (version) {
            options.version = version;
        } else {
            options.type = type;
        }
        return gulp
            .src([ './package.json', './bower.json' ])
            .pipe($.bump(options))
            .pipe(gulp.dest('./'));
    });

Execute:

    gulp version --version=2.0.0
    gulp version --type=minor

 

 

15. Unit Testing with Jasmine and Karma

  • karma is Test Runner for JavaScript, a tool that allows you to execute JS code in multiple real browsers
  • karma is A Karma plugin. used to generate code coverage.
  • Task singleRun refers to execute tests once, it can fail a build but perfect for continuous integration
  • Task alwaysRun refers to execute tests and stay alive, monitors changes in file and re-run with each change
  • Please make sure karma.config.js file is included in your project before executing gulp task
  • __dirname is global object of NodeJS for the name of the directory that the currently executing script resides in

Pre Install:

    npm install --save-dev gulp

Install:

    npm install --save-dev karma phantomjs karma-coverage karma-growl-reporter karma-phantomjs-launcher  karma-firefox-launcher karma-ie-launcher karma-chrome-launcher karma-jasmine jasmine

Code:

    var gulp = require('gulp');

    gulp.task('singleRun', function (done) {
        startTests(true, done);
    });

    gulp.task('alwaysRun',  function (done) {
        startTests(false, done);
    });

    function startTests(singleRun, done) {
        var karma = require('karma').server;
        var excludeFiles = [];
        var serverSpecs = 'tests/**/*.spec.js'

        karma.start({
            configFile: __dirname + '/karma.config.js',
            exclude: excludeFiles,
            singleRun: !!singleRun
        }, karmaCompleted);

        function karmaCompleted(karmaResult) {
            if (karmaResult === 1) {
                done('karma: tests failed with code ' + karmaResult);
            } else {
                done();
            }
        }
    }

Execute:

    gulp singleRun
    gulp alwaysRun

 

 

16. E2E Testing with Protractor

  • Protractor is an end-to-end test framework for AngularJS applications
  • It execute tests against your application running in a real browser, interacting with it as a real life user.
  • Make sure you have updated version of webdriver-manager otherwise execute below command in cmd before executing gulp task
webdriver-manager update --standalone
  • In protractor.config.js file mention file path to the selenium server jar, path of chrome driver and comment out address of a running selenium server before executing gulp task

 

seleniumServerJar: './node_modules/protractor/selenium/selenium-server-standalone-2.48.2.jar',
chromeDriver: './node_modules/protractor/selenium/chromedriver.exe',
//seleniumAddress: 'http://localhost:4444/wd/hub',
  • Start webdriver-manager in seperate console before executing gulp task
webdriver-manager start
  • __dirname is global object of NodeJS for the name of the directory that the currently executing script resides in

Pre Install:

    npm install --save-dev gulp

Install:

    npm install --save-dev gulp-protractor protractor

Code:

    var gulp = require('gulp');
    var protractor = require("gulp-protractor").protractor;

    gulp.task('e2e', function (done) {
        gulp.src(__dirname + './protractor_test/')
        .pipe(protractor({
            configFile: './protractor.config.js',
            args: ['--baseUrl', 'http://127.0.0.1:8000']
        }))
        .on('error', function (e) { throw e })
    });

Execute:

    gulp e2e

 

 

17. Setting up test runner on HTML

  • Use gulp-jasmine-browser in order to execute jasmine tests in a browser using gulp.
  • In order to check output, type following url in browser window: http://localhost:8888/

Pre Install:

    npm install --save-dev  

Install:

    npm install --save-dev gulp-jasmine-browser

Code:

    var gulp = require('gulp');
    var jasmineBrowser = require('gulp-jasmine-browser');
    var config.files = ['./scripts/lib/angular/angular.js',
        './scripts/lib/angular/angular-mocks.js',
        './scripts/app/myService.services.js',
        './scripts/app/myService.services-spec.js',];

    gulp.task('jasmineBrowser', function () {
        return gulp.src(config.files)
          .pipe(jasmineBrowser.specRunner())
          .pipe(jasmineBrowser.server({ port: 8888 }));
    });

Execute:

    gulp jasmineBrowser

 

Angular’s jqLite

Standard

Following jquery functions are available in angular jqlite: