Click here to Skip to main content
15,867,453 members
Articles / Web Development / HTML

Developing and Deploying an Angular 2 Application with Visual Studio 2015

Rate me:
Please Sign up or sign in to vote.
4.95/5 (37 votes)
13 Aug 2016CPOL30 min read 111.2K   3.1K   43   20
Angular 2 with TypeScript

Introduction

As a teenager, I loved playing the arcade video game Space Invaders; created and released in 1978. It was one of the forerunners of modern video gaming and helped expand the video game industry from a novelty to a global industry. When first released, Space Invaders was very successful. It was probably my first exposure to computer technology. Back then, I didn't know that I would end up being a software developer myself.

If you are a Microsoft developer and you have been following the Microsoft developer space of late, you may have thought to yourself, “We are being invaded!” These realizations are none the more evident than with the proliferation of the MEAN stack and it's impact on the Microsoft development space. The MEAN stack is a free and open-source JavaScript software stack for building dynamic web sites and web applications. The MEAN stack makes use of MongoDB, Express.js, Angular, and Node.js. Because all of the components of the MEAN stack support programs written in JavaScript, MEAN applications can be written in one language for both server-side and client-side execution environments.

Microsoft developers are now seeing MEAN stack family member Node.js enter their development world. Node.js brings event driven programming, enabling the development of both web servers and various tooling libraries, frameworks and components written in JavaScript to help accelerate and facilitate the development of web-based applications.

Thousands of open source libraries have been built for Node.js, most of which are hosted on the NPM website.

One of my favorite MEAN stack technologies for the last few years has been AngularJS for Single Page Applications (SPA). AngularJS is by far one the most popular JavaScript frameworks available today for creating front-end web applications.

Due to advancements in web technology and browsers, Google, the developer of AngularJS decided a few years back to rewrite AngularJS from scratch. In partnership with Microsoft, AngularJS is being rebranded and rewritten using TypeScript as Angular 2. Angular 2 and TypeScript now brings object oriented web development to the client-side of a web application, in a syntax that is strikingly close to C# for Microsoft developers.

Dare I say that someday perhaps, all web applications in the future will be developed entirely in scripting technologies such as JavaScript and TypeScript from the front-end all the way to the back-end. Anything is possible. For now, it is safe to say that Microsoft developers can continue to build their web applications on top of the Microsoft .NET framework. But beware, the invasion has started - long live space invaders. This article will dip its feet into the MEAN stack water a little and walk through the development and deployment of an application using Angular 2 for the front-end application and using Visual Studio Professional 2015 with some Node.js tools. The back-end of the sample application will consist of Microsoft .NET C# business and data access layers utilizing Microsoft's .NET Web API integrated with Microsoft Entity Framework and SQL-Server Express.

At the time of this article, Angular 2 is still a work in progress but very stable. The sample application for this article was developed using Release Candidate 4 of Angular 2. Release Candidate 5 has just been released so the final release of Angular 2 should be coming out before the end of the year.

Visual Studio 2015 vs Visual Studio Code

In 2015, Microsoft released a free editor for developers called Visual Studio Code, a lightweight cross-platform editor for writing modern web and cloud applications that will run on OS X, Linux and Windows with support for IntelliSense, debugging, and GIT. Because it is lightweight and powerful, Visual Studio Code has gained a lot of popularity. You'll see a lot of Angular 2 articles and presentations using Visual Studio Code.

Visual Studio 2015 is still the full integrated development environment that most Microsoft developers have been using since the introduction of Visual InterDev in the late 1990s. Visual Studio Code is a separate product from Microsoft and is completely different from Visual Studio 2015.

If you are a Microsoft .NET developer working as a member of a team, there is a good chance your environment workflow and software development management process is tightly bound to Microsoft's Team Foundation Server (TFS) and you are using one of the Visual Studio 2015 editions (either Professional or Enterprise edition). Visual Studio 2015 is integrated with TFS and the MsBuild tool and its Agile/Scrum templates. For these reasons, I decided to write this article using Visual Studio Professional 2015.

Overview And Goals

Image 1

For the purposes of learning Angular 2 (Release Candidate 4), the sample web application for this article will consist of the following functionality and goals:

  • Allow the user to register, login and update their user profile
  • Allow the user to create, update and browse a customer database
  • Create a Visual Studio 2015 project with an Angular 2 front-end and a Microsoft .NET backend
  • Configure Visual Studio 2015 to compile Angular 2 TypeScript files into JavaScript
  • Configure a build, bundle and minification process for both development and production release purposes
  • Provide for a mechanism for the cache busting of web application files
  • Integrate the new version of Angular UI bootstrap controls and widgets for Angular 2
  • Develop some custom homegrown controls and widgets where needed
  • Use Microsoft ASP.NET to launch the web application

There is a lot to cover with developing and deploying an Angular 2 application, so I broke this article into two parts. Part 2 called Developing An Angular 2 Application With TypeScript digs further into the source code for the sample application and some of the key aspects of Angular 2.

The sample application for this article will use Microsoft ASP.NET 4. A new version of ASP.NET has been released called ASP.NET Core. Formerly known as ASP.NET 5, ASP.NET Core is a significant redesign of ASP.NET and has a number of architectural changes that result in a much leaner and modular framework. ASP.NET Core is no longer based on System.Web.dll. I believe this is Microsoft's response to the proliferation of Node.js and its lightweight footprint and open architecture. Competition is good. Perhaps in a future article, I'll integrate Angular 2 with ASP.NET Core, in the meantime, there is a lot to learn.

Installation and the Running the Sample Application

To run the sample application after downloading and unzipping the attached source code, you'll need to run "npm install" from the command line in the root folder of the project. This assumes you already have NodeJs installed on your computer. The node_modules folder is large, so I only included the minimum node modules to compile the application. After compiling and launching the application, you can login with a username and password as follows:

UserName: bgates@microsoft.com
Password: microsoft

Optionally, you can register your own login and password.

Create an Empty ASP.NET Web Application Project

The first step to developing this application was simply to go into Visual Studio 2015 and create an empty ASP.NET web application. For the purposes of this demo, the Microsoft Web API architecture and plumbing was included with the project. The project solution will integrate both the front-end and back-end code with the Web API accepting and responding to RESTful web requests utilizing IIS Express which is integrated with Visual Studio 2015.

In a real world scenario, you will probably want to create a separate project for your backend application. A separate backend project will make the edit, compile and test process much more streamlined. This separation will also make your back-end code more reusable across various front-end applications such as separate desktop, web and mobile front-ends.

Image 2

When creating an empty ASP.NET project, you get the above structure which includes a web.config file as normal. When you deploy a web site, you often want some settings in the deployed application's Web.config file to be different from environment to environment such as development, QA, and production. For example, you might want to manage connection strings so that they point to different databases for each environment. For a front-end web application, you might want to manage the settings that point to different back-end Web API urls. Web.config transformations integrate well with Visual Studio and the MSBuild and deployment process.

Installing Angular 2 through the Node Package Manager

Angular 2 is maintained and installed as packages with the Node Package Manager (NPM). Node.js and NPM are essential to Angular 2 development. This is a departure from what Microsoft .NET developers are previous used to. The main package management tool for Microsoft .NET developers has been through the NuGet Package Management Console that comes with Visual Studio. Fortunately, Node.js gets installed when you install Visual Studio 2015. Before you can add Angular 2 to your project, you'll need the following package.json file added to the root folder of your project.

JavaScript
// package.json   
 
{
  "name": "code-project",
  "version": "1.0.0",
  "author": "Mark Caplin",
  "description": "Code Project Customer Maintenance Application",
  "license": "ISC",
  "dependencies": {
    "@angular/common": "2.0.0-rc.4",
    "@angular/compiler": "2.0.0-rc.4",
    "@angular/core": "2.0.0-rc.4",
    "@angular/forms": "0.2.0",
    "@angular/http": "2.0.0-rc.4",
    "@angular/platform-browser": "2.0.0-rc.4",
    "@angular/platform-browser-dynamic": "2.0.0-rc.4",
    "@angular/router": "^3.0.0-beta.2",
    "bootstrap": "^3.3.6",
    "es6-shim": "^0.35.0", 
    "ng2-dropdown": "0.0.4",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "systemjs": "0.19.27",
    "systemjs-builder": "^0.15.23",
    "zone.js": "^0.6.12",
    "ng2-bootstrap": "^1.0.23"
  }
}

The package.json file tells Node what to install when you run npm install from the command line. For the purpose of the sample application, Node will install Release Candidate 4 of Angular 2 as referenced in the package.json file.

Image 3

After executing "npm install" from the command line, you'll end up with a folder called node_modules. All the Angular 2 packages and supporting components are dropped into the node_modules folder under your project folder structure. NPM also manages all the dependencies for the packages that you need.

When you first navigate the node_modules folder, you'll find recursively nested node_modules folders. Most Microsoft Windows tools, utilities and shells cannot handle file and folder paths longer than 260 characters at most. This limit is easily exceeded, and once it is, install scripts start breaking and node_modules folders can no longer be deleted using conventional methods from the Microsoft Windows environment. Because of this, you will not want to include the node_modules folder in your project nor will you want to deploy any files and sub-directories directly from the node_modules folder.

SystemJS Configuration

SystemJS is a universal dynamic module loader and is also installed through NPM. SystemJS can load ES6 modules, AMD modules, CommonJS and global scripts in the browser. Like most Angular 2 demos you see online, this application will use SystemJS to start-up the Angular 2 application. Later in this article, SystemJS and its Builder tool will be used to bundle Angular 2 and the application for both development and production deployment purposes. SystemJS needs to know what packages and components are needed for the application and where to find these packages. The sample system.config.js file below was added to the root folder of the web application project.

JavaScript
// systemjs.config.js

(function (global) {

    // map tells the System loader where to look for things
    var map = {
        'application': 'application', // 'dist',
        'rxjs': 'node_modules/rxjs',
        'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
        '@angular': 'node_modules/@angular',
        'moment': 'node_modules/moment/moment.js',
        'ng2-bootstrap/ng2-bootstrap': 'node_modules/ng2-bootstrap/ng2-bootstrap.js'  
    };

    // packages tells the System loader how to load when no filename and/or no extension
    var packages = {
        'application': { main: 'main.js', defaultExtension: 'js' },
        'rxjs': { defaultExtension: 'js' },
        'angular2-in-memory-web-api': { defaultExtension: 'js' },
        'moment': 'node_modules/moment/moment.js',       
        'ng2-bootstrap/ng2-bootstrap': { defaultExtension: 'js' },       
    };

    var packageNames = [
      '@angular/common',
      '@angular/compiler',
      '@angular/core',
      '@angular/http',
      '@angular/forms',
      '@angular/platform-browser',
      '@angular/platform-browser-dynamic',
      '@angular/router',
      '@angular/router-deprecated',
      '@angular/testing',
      '@angular/upgrade',
    ];

    //
    // add package entries for angular packages in the form 
    // '@angular/common': { main: 'index.js', defaultExtension: 'js' }
    //

    packageNames.forEach(function (pkgName) {
        packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
    });

    // filterSystemConfig - index.html's chance to modify config before we register it.
    if (global.filterSystemConfig) { global.filterSystemConfig(config); }

    System.config(config);

})(this);

TypeScript Typings

Many JavaScript libraries such as jQuery, the Jasmine testing library, and Angular itself, extend the JavaScript environment with features and syntax that the TypeScript compiler doesn't recognize natively. When the compiler doesn't recognize something, it throws an error.

We use TypeScript type definition filesd.ts files — to tell the compiler about the libraries we load. A Typescript typings configuration file called typings.json needs to be added to the root of the project with the following content:

JavaScript
// typings.json

{
  "ambientDependencies": {    
    "jasmine": "registry:dt/jasmine#2.2.0+20160412134438",
    "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/
                 es6-shim.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd"
  }
}

We installed the typings tool with NPM (it's listed among the devDependencies in the package.json) and added an NPM script to run that tool automatically after the NPM installation completed. This typings tool command installs the d.ts files that we identify in the typings.json file into a folder called typings.

Index Razor View Page

Most of the Angular 2 demos you'll see online start with an index.html page to startup the application. In the sample application for this article, I decided to add a MVC Razor view page to my project called index.cshtml to start up the application. I find that the ASP.NET MVC architecture makes for a great delivery system for AngularJS and Angular 2 applications.

The razor view page allows the application to pull server-side settings from the web.config file. This makes it easy to inject values into the index page such as injecting version numbers, web api and base urls and other information into the start-up process. The razor view page also allows you to write if statements for generating the index page for both debug and production mode.

HTML
<!-- index.cshtml -->

@{
    Layout = null;

    string version = 
    typeof(CodeProjectAngular2.Portal.Global).Assembly.GetName().Version.ToString();
 
    Boolean includeBrowserSync = AppSettings["IncludeBrowserSync"]);

    string currentRoute = "/";

    // catch F5 reboot
    foreach (string key in HttpContext.Current.Request.QueryString.AllKeys) 
    {
        if (key == "CurrentRoute")
        {
            currentRoute = HttpContext.Current.Request.QueryString[key];
            break;
        }
    }
}
<!DOCTYPE html>
<html>
<head>
    <title>CodeProject Angular 2 </title>
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="-1″>
    <meta http-equiv="CACHE-CONTROL" content="NO-CACHE">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <base href="@codeProjectBaseUrl" />
    <script>
        history.pushState({}, null, "@currentRoute");
    </script>
   
    <!-- Polyfill(s) for older browsers - 06/30/2016 -->

    <!-- shim.min.js is from node_modules/corejs -->
    <script src="~/scripts/shim.min.js?version=4"></script>   

    <script src="~/scripts/zone.min.js?version=4"></script>
    <script src="~/scripts/Reflect.js?version=4"></script>
    <script src="~/scripts/system.src.js?version=4"></script>      

    @if (HttpContext.Current.IsDebuggingEnabled)
    {                       
        <script src="ng2-bootstrap.min.js?version=4"></script>
        <script src="~/Scripts/angular2-dev.min.js?version=4" ></script> 

        <!-- 2. Configure SystemJS -->
        <script src="~/systemjs.config.js"></script>
        <script>
            System.config({
                packages: { 'application': { defaultExtension: 'js' } }
            });
            System.import('application').catch(function (err) {
                 console.error(err); 
                });
        </script>
    }
  
    <link href="~/content/bootstrap.css" rel="stylesheet" />
    <link href="~/content/spinner.css" rel="stylesheet" />
    <link href="~/content/style.css" rel="stylesheet" />   

</head>

<body>
    <codeproject-application currentRoute="@currentRoute">
        <div>
            ...loading CodeProject Angular 2
        </div>
    </codeproject-application>

    @if (HttpContext.Current.IsDebuggingEnabled && includeBrowserSync == true)
    {

            <!-- BrowserSync:SNIPPET-->
                 <script type='text/javascript' id="__bs_script__">
                         //<![CDATA[
                           document.write("<script async src='http://HOST:PORT
                                           /browser-sync/browser-sync-client.js'><\/script>"
                           .replace("HOST", location.hostname)
                           .replace("PORT", parseInt(location.port) + 1));
                         //]]>
                 </script>
            <!-- BS:BrowserSyncs:END-->
    }

    @if (HttpContext.Current.IsDebuggingEnabled==false)
    {
        <script src="~/scripts/angular2.min.js?version=@version" ></script>
    }

</body>
</html>

TypeScript and the Application Directory

The first thing you will want to think about is creating an application folder for your Angular 2 application and what that directory structure will look like. The application directory will mainly contain your application HTML templates and TypeScript components. To get up and running, an initial folder structure may look as follows which includes the tradition about, contact, home and master pages.

Image 4

Automatic Version Numbers and Cache Busting

For this sample application, I wanted to keep track of versions and build numbers each time I compiled, tested and published the application using the information in the AssemblyInfo.cs file under the Properties folder. Each time the application runs, I wanted to get the latest version of the application and use the version number to help with such things as appending a version number to the end of JavaScript files that would tell the browser to get new versions of these files instead of the browser running an older version of these files from its browser cache. To make things easy, I downloaded an Automatic Version plugin for Visual Studio Professional 2015 from here.

That will automatically increment assembly version(s) for C# and VB.NET projects. The download installs the plugin into the Tools menu called Automatic Version Settings. The plugin comes with a configuration tool that allows you to configure your major and minor build numbers that will update your AssemblyInfo.cs file automatically on each compile. Optionally, you can manually update the version number or use something like Microsoft's TFS to manage your build numbers in a fully integrated continuous build and configuration management environment.

Polyfills For Older Browsers

One of the things we need to add to our Angular 2 project are polyfill scripts. In web development, a polyfill is code that implements a feature on web browsers that do not support the feature. Most often, it refers to a JavaScript library that implements an HTML5 web standard, either an established standard (supported by some browsers) on older browsers, or a proposed standard (not supported by any browsers) on existing browsers.

Formally, a polyfill is a shim for a browser API. Polyfills allow web developers to use an API regardless of whether it is supported by a browser or not, and usually with minimal overhead. Typically, they first check if a browser supports an API, and use it if available, otherwise it uses its own implementation. Polyfills themselves use other, more supported features, and thus different polyfills may be needed for different browsers.

For Angular 2 applications, a handful of polyfills are needed. For the sample application, I created a Scripts folder, and copied and added the required JavaScript polyfill files to this folder from the node_modules folder - noting that I didn't want to include anything directly from the node_modules folder when going into production. The index page makes reference to these files in script tags as follows:

HTML
<!-- index.cshtml -->

<!-- Polyfills for older browers -->

<!-- from node_modules\core-js\client-->   <script src="~/Scripts/core.min.js"></script>
<!-- from node_modules\zone.js\dist-->     <script src="~/Scripts/zone.min.js"></script>
<!-- from node_modules\reflect-metadata--> <script src="~/Scripts/Reflect.js"></script>
<!-- from node_modules\systemjs\dist-->    <script src="~/Scripts/system.src.js"></script>

Every Angular 2 application needs a main bootstrap component, a root application component, a master page and a routing component. Below is some boilerplate code for these components:

JavaScript
/* main.ts */

///<reference path="../typings/browser.d.ts"/>

import { bootstrap }    from '@angular/platform-browser-dynamic';
import { disableDeprecatedForms, provideForms } from "@angular/forms";

import { ApplicationComponent } from './application.component';
import { applicationRouterProviders } from "./application.routes";
import { enableProdMode} from '@angular/core';

enableProdMode();

bootstrap(AppComponent, [

    applicationRouterProviders,
    disableDeprecatedForms(),
    provideForms()

]);

/* application.component.ts */

import { Component } from '@angular/core';
import { HTTP_PROVIDERS, HTTP_BINDINGS } from '@angular/http';

import { MasterComponent } from './master.component';
import 'rxjs/Rx';

@Component({
    selector: 'code-project-application',
    template: '<master></master>',
    directives: [MasterComponent],
    providers: [HTTP_BINDINGS,HTTP_PROVIDERS]
})

export class AppComponent {

    constructor() {}

}

/* applications.routes.ts */

import { provideRouter, RouterConfig } from "@angular/router";

import { AboutComponent } from './home/about.component';
import { ContactComponent } from './home/contact.component';
import { HomeComponent } from './home/home.component';

const routes: RouterConfig = [

    { path: 'home/about', component: AboutComponent },
    { path: 'home/contact', component: ContactComponent },
    { path: 'home/home', component: HomeComponent }
];

export const applicationRouterProviders = [

    provideRouter(routes)

];

/* master.component.ts */

import { Component, OnInit } from '@angular/core';
import { HTTP_PROVIDERS } from '@angular/http';
import { ROUTER_DIRECTIVES } from '@angular/router';

@Component({
    selector: 'master',
    templateUrl: 'application/master.component.html',
    directives: [ROUTER_DIRECTIVES]
})

export class MasterComponent implements OnInit {

    constructor() {}

    public ngOnInit() {

    }  
}

TypeScript and Visual Studio 2015

If you are a C# developer, you will love TypeScript to develop Angular 2 applications. It has the look and feel of C# on the client-side, including support for strongly-typed objects and properties. TypeScript starts from the same syntax and semantics of JavaScript. TypeScript compiles to clean, simple JavaScript code which runs on any browser, in Node.js, or in any JavaScript engine that supports ECMAScript 3 (or newer).

TypeScript is integrated with Visual Studio 2015 and comes with full IntelliSense of classes and components. When you save a TypeScript file and when configured, Visual Studio 2015 will automatically compile your TypeScript file into a JavaScript file.

Additionally, when you build your application from Visual Studio, Visual Studio 2015 will recompile all your TypeScript files. If there are any TypeScript syntax errors, the build will fail. This is awesome. Now as a developer, you can take comfort in knowing that your JavaScript has been checked for syntax errors and type errors during compile time instead of finding the errors in production.

When you first compile your application, TypeScript will compile with errors in Visual Studio. An error you may see will look as follows: Error TS1219 Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option to remove this warning.

To fix this, you will need to edit your project file and add a few TypeScript options. You can do this by editing the file from the Windows folder or by unloading your project in Visual Studio (Right-click on the project name and select Unload Project, and select edit projectname.csproj file, make your changes and reload the project);

The following three options are needed in your project file:

XML
<!-- CodeProjectAngular2.csproj -->

<TypeTypeScriptModuleResolution>NodeJs</TypeScriptModuleResolution>
<TypeScriptExperimentalDecorators>true</TypeScriptExperimentalDecorators>
<TypeScriptEmitDecoratorMetadata>true</TypeScriptEmitDecoratorMetadata>

Your project file should look like something as follows which includes adding the three additional settings and making a copy of these settings for each configuration you might have (dev, qa, production, release). The TypeScript compiler will read these settings during compilation.

XML
<!-- CodeProjectAngular2.csproj -->

<PropertyGroup Condition="'$(Configuration)|$(Platform)'== Debug|AnyCPU'"> 
<TypeScriptTarget>ES5</TypeScriptTarget>
<TypeScriptJSXEmit>None</TypeScriptJSXEmit>
<TypeScriptCompileOnSaveEnabled>True</TypeScriptCompileOnSaveEnabled>
<TypeScriptNoImplicitAny>False</TypeScriptNoImplicitAny>
<TypeScriptModuleKind>CommonJS</TypeScriptModuleKind>
<TypeScriptRemoveComments>False</TypeScriptRemoveComments>
<TypeScriptOutFile />
<TypeScriptOutDir />
<TypeScriptGeneratesDeclarations>False</TypeScriptGeneratesDeclarations>
<TypeScriptNoEmitOnError>True</TypeScriptNoEmitOnError>
<TypeScriptSourceMap>True</TypeScriptSourceMap>
<TypeScriptModuleResolution>NodeJs</TypeScriptModuleResolution>
<TypeScriptExperimentalDecorators>true</TypeScriptExperimentalDecorators>
<TypeScriptEmitDecoratorMetadata>true</TypeScriptEmitDecoratorMetadata>
<TypeScriptMapRoot />
<TypeScriptSourceRoot />
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">   
<TypeScriptTarget>ES5</TypeScriptTarget>
<TypeScriptJSXEmit>None</TypeScriptJSXEmit>
<TypeScriptCompileOnSaveEnabled>True</TypeScriptCompileOnSaveEnabled>
<TypeScriptNoImplicitAny>False</TypeScriptNoImplicitAny>
<TypeScriptModuleKind>CommonJS</TypeScriptModuleKind>
<TypeScriptRemoveComments>False</TypeScriptRemoveComments>
<TypeScriptOutFile />
<TypeScriptOutDir />
<TypeScriptGeneratesDeclarations>False</TypeScriptGeneratesDeclarations>
<TypeScriptNoEmitOnError>True</TypeScriptNoEmitOnError>
<TypeScriptSourceMap>True</TypeScriptSourceMap>
<TypeScriptModuleResolution>NodeJs</TypeScriptModuleResolution>
<TypeScriptExperimentalDecorators>true</TypeScriptExperimentalDecorators>
<TypeScriptEmitDecoratorMetadata>true</TypeScriptEmitDecoratorMetadata>
<TypeScriptMapRoot />
<TypeScriptSourceRoot />    
</PropertyGroup> 

After adding these new settings, your TypeScript file will automatically compile upon saving. Unfortunately, when you go to build your entire application, you will end up with the following TypeScript compiler error:

error TS6063:Build:Argument for '--moduleResolution' option must be 'node' or 'classic'.

In the project settings, we needed to set the TypeScript Compiler moduleResolution parameter to "NodeJs" to make the compile-on-save work. The problem is that the TypeScript compiler when run during an MsBuild needs the moduleResolution setting to be set to 'node' for the TypeScript compiler. This seems to be a bug in the Visual Studio 2015 settings. To override and fix this - until a patch comes along - you need to also add the following to the bottom of your project file which will be executed during the build which will override the TypeScript compiler moduleResolution option to the setting it needs to run successfully.

XML
<!-- CodeProjectAngular2.csproj -->

<PropertyGroup> 
    <CompileTypeScriptDependsOn> 
        SpoofTypeScriptModuleResolution;$(CompileTypeScriptDependsOn) 
    </CompileTypeScriptDependsOn> 
</PropertyGroup> 
<Target Name="SpoofTypeScriptModuleResolution"> 
   <PropertyGroup> 
      <TypeScriptBuildConfigurations>$(
        TypeScriptBuildConfigurations.Replace
        ('--moduleResolution NodeJs', '--moduleResolution node')) 
      </TypeScriptBuildConfigurations> 
   </PropertyGroup> 
   <Message Text="Options: $(TypeScriptBuildConfigurations)" /> 
</Target> ​​​​​

Bootstrap and Angular 2 UI

We are now ready to run our application. But before we do that, we should add some Bootstrap styling to our application. In AngularJS, we had Angular UI which came with a toolbox of directives and widgets that enhanced the AngularJS front-end, including date pickers, alert boxes and various other widgets.

For Angular 2, I came across ng2-bootstrap (supported by Valor Software) that has the equivalent set of directives for Angular 2. Both the standard bootstrap css file and ng2-bootstrap got installed through the initial npm install and exists in the node_modules folder. The following two tags were added to the index page:

HTML
<!-- index.cshtml -->

<script src="~/scripts/ng2-bootstrap.min.js?version=4"></script>
<link href="~/application/content/bootstrap.css?version=4" rel="stylesheet" /> 

<!--node_modules\bootstrap\dist\css-->

The bootstrap css file was copied to the content folder of the project. In development mode, the index page will make reference to the ng2-bootstrap directives that are bundled together in a single file.

Running the Application

The sample application for this article starts up by running the index razor page which makes a reference to SystemJS. SystemJS locates the main Angular 2 bootstrap file main.js and bootstraps Angular 2 to launch the application.

HTML
<!-- index.cshtml -->

<script src="~/systemjs.config.js"></script>
<script>
    System.config({
        packages: { 'application': { defaultExtension: 'js' } }
    });
    System.import('application').catch(function (err) { 
        console.error(err); }
    );
</script>   

When you first run the application, especially in Internet Explorer, you'll notice it takes a while for the application to load. You'll ask yourself, "what is going on, I thought Angular 2 was suppose to be faster than AngularJS?". Digging into this matter, I decided to open up the Network tab in the Internet Explorer development tools and I reloaded the application to see what was going on.

Image 5

The Network tab revealed something a little shocking. The initial running of the application made 446 requests to IIS Express. Turns out that when you launch the application with SystemJS in the index page, it traverses the entire application and loads all the references and dependencies from the Angular 2 node_modules directory, including your own application code.

Even though most of the content was already cached, the number of hits to IIS Express caused up to a 30 second delay in loading the application. Something needed to be done about this. The first question was, how can the network requests be reduced? Obviously, this would be unacceptable in production.

Bundling Angular 2 for Development with Gulp

After looking for ways to streamline the development process and speed up the initial load of application and reduce the number of requests to IIS Express, it occurred to me that I didn't really need to reload all the Angular 2 components every time the application started. I could just bundle all the Angular 2 components into a single file and thus making one request to bring Angular 2 to the browser.

Gulp is a toolkit that helps you automate painful or time-consuming tasks in your development workflow. Gulp integrates into all major IDEs and platforms including PHP, .NET, Node.js, Java, and other platforms. Gulp uses NPM modules to do anything you want with over 2000 plugins for streaming file transformations.

With references in the package.json file, Gulp and the plugins that I wanted were installed when I ran npm install from the command line. To run Gulp tasks, you need to create a gulpfile.js file and add it to the root of your project.

The gulpfile below runs two tasks and uses the SystemJS Builder bundle method to take all the individual Angular 2 components, and compile them into a CommonJS format in a temporary directory under the scripts folder in the project. After compiling the Angular 2 components, an additional Gulp task is executed that pipes all the code into a single concatenated and minified JavaScript file.

One of the key features that makes things simple in Gulp is its piping and streaming functionality. Gulp makes use of pipes for streaming data that needs to be processed. Input into one function can be processed and piped as output to another function as needed. Essentially, you can execute a series of steps in a single command and all in memory before writing the output to disk.

JavaScript
// gulpfile.js 

var gulp = require('gulp');
var Builder = require('systemjs-builder');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var runSequence = require('run-sequence');

var appProd = './scripts';
var appDevTemp = './scripts/temp/angular2';

//
// the following series of tasks builds each component of angular 2 into a temp directory 
// for caching in development mode
//
gulp.task('@angular.js', function () 

    var SystemBuilder = require('systemjs-builder');

    var builder = new SystemBuilder('./', 'systemjs.config.js');

    builder.bundle('@angular/core', appDevTemp + '/core.js');
    builder.bundle('@angular/compiler',  appDevTemp + '/compiler.js');
    builder.bundle('@angular/forms', appDevTemp + '/forms.js');
    builder.bundle('@angular/common', appDevTemp +  '/common.js');
    builder.bundle('@angular/http', appDevTemp + '/http.js');
    builder.bundle('@angular/router', appDevTemp + '/router.js');
    builder.bundle('@angular/platform-browser', appDevTemp + '/platform-browser.js');
    builder.bundle('@angular/platform-browser-dynamic', 
                    appDevTemp + '/platform-browser-dynamic.js');
    builder.bundle('rxjs/Rx', appDevTemp + '/rxjs.js');
    return;

});
//
//  minify the development build for angular 2
//
gulp.task('buildForDevelopment', ["@angular.js"],
    function () {
        return gulp.src( appDevTemp +'/*.js').pipe(uglify())
                  .pipe(concat('angular2-dev.min.js')).pipe(gulp.dest(appProd));
}); 

The end result produced from the above Gulp tasks is a single minified JavaScript file of Angular 2 that can be referenced with a script tag. The below code snippet was added to the index razor view page that conditionally adds the script tag reference to the angular2-dev.min.js file when running in debug mode. When a new version of Angular 2 is installed, you just need to re-run the bundle tasks again.

HTML
<!-- index.cshtml -->

@if (HttpContext.Current.IsDebuggingEnabled==true)
{   
    <script src="~/Scripts/angular2-dev.min.js?version=4" ></script> 
}

You can execute Gulp tasks through the Visual Studio 2015 Task Runner Explorer window as shown below. You can also tell Visual Studio 2015 to execute tasks automatically by binding tasks to certain events (such as the build event) through the task runner window. Additionally, you can simply run a Gulp task by typing "gulp taskname" from the command line.

Image 6

After creating this bundled file of Angular 2 components, I launched the sample application again, and the number of requests to IIS Express was greatly reduced and the application launched much quicker, from about 30 seconds to under 10 seconds.

The SystemJS builder tool is a great tool and provides comprehensive support for compiling all module formats into a single bundle.

BrowserSync Gulp Watcher

There are a lot of plugins and tools that you can implement with Gulp that will enhance your development experience. Another favorite of mine is the Gulp watcher function.

The Gulp watcher function comes built into Gulp. You can tell Gulp to watch for certain files changes in your project and automatically refresh your web browser with the changes without you having to manually hit F5 in the browser. There are various browser plugins that help enable this functionality such as Livereload and BrowserSync.

The sample application for this article uses BrowserSync. To get started, you need to set-up a Gulp task to run that looks for file system changes in your project as follows:

JavaScript
// gulpfile.js

var browserSync = require('browser-sync').create();
var iisPort = 55059;
//
//  configure browser sync
//
gulp.task('sync', function () {

    browserSync.init({
        port: iisPort + 1
    });

    gulp.watch("application/**/*.html").on('change', browserSync.reload);
    gulp.watch("application/**/*.js").on('change', browserSync.reload);
    gulp.watch("*.cshtml").on('change', browserSync.reload);

});

Setting up the Gulp browser sync task requires the set-up of an IIS port and telling Gulp which file patterns to watch for changes on. In the sample above; the task references the IIS port that the sample application is using for IIS Express and increments that port number by one for listening for changes on a separate port. The Gulp watcher task is being set-up to look for changes for both JavaScript and HTML files under the application directory tree. Changes to the index razor page are also being watched.

HTML
<!-- index.cshtml -->

@{
    Boolean includeBrowserSync =  ConfigurationManager.AppSettings["IncludeBrowserSync"];   
}

@if (HttpContext.Current.IsDebuggingEnabled == true && includeBrowserSync == true)
  {
    <!-- BrowserSync:SNIPPET-->
    <script type='text/javascript' id="__bs_script__">
    //<![CDATA[
    document.write
    ("<script async src='http://HOST:PORT/browser-sync/browser-sync-client.js'<\/script>"
    replace("HOST", location.hostname).replace("PORT", parseInt(location.port) + 1));
    //]]>
    </script>
    <!-- BS:BrowserSyncs:END-->
 }

The next step is to set-up a listener in the browser by injecting a Script tag into the index razor view page that enables browser sync listening. The sample application will reference a flag in the web.config file that can be turned on and off. When in debug mode and with the includeBrowserSync flag is set to true, the script tag implements the browser sync listener.

Now to get everything running, you just need to type "gulp gulptaskname" at the command line and the watcher will run indefinitely until you shutdown the gulp process from the command line.

To help with busting the browser cache during development, I added a date time stamp to the end of all the HTML templates in the components. This way, I was assured that the browser will pick up the latest changes to any HTML template. Later during the production build, this time stamp will be removed from the component.

JavaScript
export var debugVersion = "?version=" + Date.now();
@Component({
   templateUrl: 'application/home/about.component.html' + debugVersion  
})

Maintaining the Current Route during Browser Refresh through MVC

One of the side-effects of using MVC as a application bootstrapper, is that every browser refresh performed weather through the automatic browser syncing tool or by manually refreshing the browser yourself is that the refresh initiates a new server request which goes back to IIS. In order to maintain the route of the current page, I had to hack into the MVC routing pipeline.

I wanted to redirect all web requests back to the index razor view page without creating folders for MVC controllers, actions and views. Basically, I wanted a controller-less MVC web application and grab the current route and pass it along to the index razor view page so that the current route could be retained.

To get this to work, I came across Brent Jenkin's MVC Controllerless CodeProject article.

This was exactly what I was looking for. After adding the code from Brent's article to the AppStart directory, I simply modified the DispatchRequest method and redirected the request back to the default index razor view page and appended the current route to the query string.

C#
private void DispatchRequest
(IControllerFactory controllerFactory, string controller, string action)
{
     string currentRoute = _requestContext.HttpContext.Request.CurrentExecutionFilePath;
     string defaultPage = AppSettings["DefaultPage"].ToString();

     _requestContext.HttpContext.Response.Redirect(defaultPage + 
                     "?referral" + currentRoute + "&" + "CurrentRoute=" + currentRoute);
}

The Controllerless route plumbing is configured in the RouteConfig class when MVC registers its routing configuration.

C#
public class RouteConfig
{
     public static void RegisterRoutes(RouteCollection routes)
     {
          routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

          var route = new Route(
               "{controller}/{action}/{id}", new RouteValueDictionary(new
                    {
                         controller = "Home",
                         action = "Index",
                         id = UrlParameter.Optional
                    }),
          new CodeProjectAngular2.Portal.ControllerLess.Mvc.ControllerLessRouteHandler());

          routes.Add(route);
     }
}

The index razor view page first checks the query string for the current route and if provided, the route is injected into the browser's history through the pushState method in the page header. Finally, the current route is injected into the Angular 2 application through an Angular 2 input parameter. The Angular 2 application will simply navigate to the current route if the value is provided for.

HTML
// index.cshtml
@{
    string currentRoute = "/";

    foreach (string key in HttpContext.Current.Request.QueryString.AllKeys) 
    {
        if (key == "CurrentRoute")
        {
            currentRoute = HttpContext.Current.Request.QueryString[key];
        }
    }
}

<script>
    history.pushState({}, null, "@currentRoute");
</script>

<codeproject-application title="@title" 
currentRoute="@currentRoute" version="@version">
    <div>
        ...loading
    </div>
</codeproject-application>

// master.component.ts code snippet

if (this.currentRoute == "/") {
     this.router.navigate(['/home/home']);
            return;
     }
     else {           
        this.router.navigate([this.currentRoute]);
     }  
}

Bundling the Entire Sample Application for Production

After developing the sample application, I was ready to deploy the application to a production IIS web server. One of the latest trends for packaging applications for production use is to bundle the entire application into a single JavaScript file, including the associated HTML templates and CSS files. The big question I had was how to package up all the components of the application, including Angular 2 into a single bundle.

One of the advantages of bundling your entire application into a single JavaScript file is that there would be only a single hit to your web server that brings down the entire application to the client where it can be cached. Subsequent requests for application resources would come from the client and thus significantly reducing the network traffic on your web server.

In the below Angular 2 component for the about page, you'll notice that the HTML template is referenced through a templateUrl property. This is great for development purposes because it allows you to separate the HTML from the TypeScript code.

JavaScript
// about.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
    templateUrl: 'application/home/about.component.html'
})

export class AboutComponent implements OnInit {

    public title: string;

    constructor() { }

    public ngOnInit() {
        this.title = "About";
    }
}

For production purposes, I wanted to replace the templateUrl tag and inject the HTML template directly into the TypeScript component using the template property of the @Component annotation. This would facilitate the bundling of the entire application into a single JavaScript file.

When running the application in production, I didn't want any dependency of a module loader such as SystemJS. I just wanted to insert a script tag into the index razor page that references the production JavaScript file for the entire application.

For the production build, I revisited the SystemJS builder tool which offers the ability to create self-executing bundles that can run without needing SystemJS present at all by embedding a micro-loader implementation into a bundled JavaScript file.

The first step to the bundling process was to inject the HTML templates into their associated JavaScript files. The good thing with Visual Studio is that it will recompile all the application TypeScript files into JavaScript when building the application.

There are a few Gulp plugins that can perform HTML injection such as gulp-inline-ng2-template. As an experiment, I wanted to write my own custom Gulp task to perform HTML injection. The cool thing about writing Gulp tasks, is that you can write pure JavaScript code and create all kinds of custom tasks. Of course, I do recommend reading the Gulp documentation. Most likely, there is already a Gulp plugin that will perform the task that you need.

JavaScript
// gulpfile.js

gulp.task("buildForProduction", function () {

    console.log('injectHTML started');

    var i = 0;

    return gulp.src('application/**/*.js').pipe(foreach(function (stream, file) {

        var name = file.path;
        var fileStream = fs.readFileSync(name, "utf8");

        var fileContents = fileStream.split("\n");
        var rows = fileContents.length;

        var output = "";

        for (var i = 0; i < rows; i++) {

            var currentLine = fileContents[i];
            var outputLine = currentLine;

            if (currentLine.indexOf('application/') > -1) {

                if (currentLine.indexOf('templateUrl') > -1) {

                    currentLine = currentLine.replace("+ exports.debugVersion", "");

                    var start = currentLine.indexOf("application");
                    var end = currentLine.indexOf(".html");
                    var lengthOfName = end - start;
                    var htmlFileName = "./" + currentLine.substr(start, lengthOfName) + ".html";

                    var comma = currentLine.indexOf(",");

                    try {
                        var htmlContent = fs.readFileSync(htmlFileName, "ASCII");

                        htmlContent = htmlContent.replace('/[\x00-\x1F\x80-\xFF]/', '');
                        htmlContent = htmlContent.replace("o;?", "");
                        htmlContent = htmlContent.replace(/"/g, '\\"');
                        htmlContent = htmlContent.replace(/\r\n/g, '');
                        htmlContent = "\"" + htmlContent + "\"";

                        currentLine = "template: " + htmlContent;
                        if (comma > -1) currentLine = currentLine + ",";
                        outputLine = currentLine;

                    }
                    catch (err) {
                        console.log(htmlFileName + " not found.")
                    }
                }
            }
            output = output + outputLine;
        }

        streams = [];
        var stream = source("inject.js");
        var streamEnd = stream;
        stream.write(output);
        process.nextTick(function () {
            stream.end();
        });

        streamEnd = streamEnd.pipe(vinylBuffer()).pipe
                    (concat(name)).pipe(gulp.dest("./application"));
        return stream;

    })).on('end', function () {       

        var builder = new Builder('./', 'systemjs.config.js');
        builder.buildStatic('./application/main.js', 
               'scripts/angular2.min.js', { minify: true, sourceMaps: false }
        ).then(function () {
            console.log('Build static complete');
        });    
    });
});         

In the above Gulpfile.js example, a Gulp task is executed which first injects the HTML template associated with each JavaScript component. The task runs using the gulp.src method that navigates the entire application directory structure looking for JavaScript files and processes each file separately.

The source of each file is piped into a foreach loop where the task looks for the templateUrl line of code, extracts out the file name of the HTML template and reads in the HTML file and replaces the templateURL line with inline HTML. The templateUrl property is replaced with the template property.

Once the injection part of the task has been completed, the "buildForProduction" task continues on and uses the SystemJS Builder tool. When creating the development bundle, the "bundle" method of the builder tool was used. For production, I wanted to create a self-executing bundle that can run without needing SystemJS present in production. Using the "buildStatic" method of the SystemJS Builder will embed a micro-loader at the end of the JavaScript bundle for you.

To configure the SystemJS Builder, you give it a reference to the systemjs.config.js file so it can know where to locate all the application components. The buildStatic method has two parameters. The first parameter is the name of the main component of your application, in this case main.js. The main component is the top level root component of your application. What the buildStatic method does from there is traverse the entire application for all its components and dependencies and combines all the components and their dependencies in the proper order; which is one of the main advantages of module loaders.

The second parameter tells the SystemJS builder tool the destination and name of the file where all the code will be bundled into. Finally, the SystemJS builder tool has an minify option that when set to true will create a minified JavaScript file for you.

All this worked great until the builder tool reached references to the ng2-bootstrap components. For some reason, in the node_modules directory for the ng2-bootstrap components, the build could not find the paths to all its components and dependencies. This forced me to edit the ng2-bootstrap components in the node_modules directory and provide stronger path information including adding the ".js" extension in all the require statements.

For example, I had to change the below ng2-bootstrap.js with full paths and JavaScript extensions in the require statements. Perhaps later, I will find out what the problem really was and this will be fixed in the future.

JavaScript
"use strict";
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}

var accordion_1 = require('./components/accordion/accordion.component.js');
var alert_1 = require('./components/alert/alert.component.js');

var buttons_1 = require('./components/buttons/button-checkbox.js');
var buttons_2 = require('./components/buttons/button-radio.js');

var carousel_1 = require('./components/carousel/carousel.component.js');
var collapse_1 = require('./components/collapse/collapse.directive.js');

var datepicker_1 = require('./components/datepicker/date-formatter.js');
var datepicker_2 = require('./components/datepicker/inner.component.js');
var datepicker_3 = require('./components/datepicker/popup.component.js');
var datepicker_4 = require('./components/datepicker/date.component.js');
var datepicker_5 = require('./components/datepicker/month.component.js');
var datepicker_6 = require('./components/datepicker/year.component.js');

var dropdown_1 = require('./components/dropdown/dropdown-menu.js');
var dropdown_2 = require('./components/dropdown/dropdown-toggle.js');
var dropdown_3 = require('./components/dropdown/dropdown.directive.js');
var dropdown_4 = require('./components/dropdown/dropdown.service.js');

var modal_1 = require('./components/modal/modal.component.js');
var pagination_1 = require('./components/pagination/pagination.js');
var progressbar_1 = require('./components/progressbar/progressbar.js');
var rating_1 = require('./components/rating/rating.component.js');

var tabs_1 = require('./components/tabs/tab-heading.directive.js');
var tabs_2 = require('./components/tabs/tab.directive.js');
var tabs_3 = require('./components/tabs/tabset.component.js');

var timepicker_1 = require('./components/timepicker/time.component.js');

var tooltip_1 = require('./components/tooltip/tooltip-container.js');
var tooltip_2 = require('./components/tooltip/tooltip-options.class.js');
var tooltip_3 = require('./components/tooltip/tooltip.directive.js');

var typeahead_1 = require('./components/typeahead/typeahead.component.js');
var typeahead_2 = require('./components/typeahead/typeahead-options.js');
var typeahead_3 = require('./components/typeahead/typeahead-utils.js');
var typeahead_4 = require('./components/typeahead/typeahead.directive.js');

var components_helper_service_1 = 
    require('./components/utils/components-helper.service.js');

__export(require('./components/accordion.js'));
__export(require('./components/alert.js'));
__export(require('./components/buttons.js'));
__export(require('./components/carousel.js'));
__export(require('./components/collapse.js'));
__export(require('./components/datepicker.js'));
__export(require('./components/modal.js'));
__export(require('./components/dropdown.js'));
__export(require('./components/pagination.js'));
__export(require('./components/progressbar.js'));
__export(require('./components/rating.js'));
__export(require('./components/tabs.js'));
__export(require('./components/timepicker.js'));
__export(require('./components/tooltip.js'));
__export(require('./components/typeahead.js'));
__export(require('./components/position.js'));
__export(require('./components/common.js'));
__export(require('./components/ng2-bootstrap-config.js'));

exports.BS_VIEW_PROVIDERS = [{ 
    provide: components_helper_service_1.ComponentsHelper, 
    useClass: components_helper_service_1.ComponentsHelper }];

Object.defineProperty(exports, "__esModule", { value: true });
exports.default = {
    directives: [
        alert_1.AlertComponent,
        accordion_1.ACCORDION_DIRECTIVES,
        buttons_1.BUTTON_DIRECTIVES,
        carousel_1.CAROUSEL_DIRECTIVES,
        collapse_1.CollapseDirective,
        datepicker_1.DATEPICKER_DIRECTIVES,
        dropdown_1.DROPDOWN_DIRECTIVES,
        modal_1.MODAL_DIRECTIVES,
        pagination_1.PAGINATION_DIRECTIVES,
        progressbar_1.PROGRESSBAR_DIRECTIVES,
        rating_1.RatingComponent,
        tabs_1.TAB_DIRECTIVES,
        timepicker_1.TimepickerComponent,
        tooltip_1.TOOLTIP_DIRECTIVES,
        typeahead_1.TYPEAHEAD_DIRECTIVES
    ],
    providers: [
        components_helper_service_1.ComponentsHelper
    ]
};

Once I had the entire application bundled into a single JavaScript file, I just needed to add a script tag at the bottom of the body in the index razor page to reference it. In the code snippet below, the angular2.min.js file is loaded when running in production mode. The application version number is appended to the JavaScript file so that the client's browser cache is refreshed (cache busting) with the new version of the file.

To simulate production mode to test this, you can edit the web.config file and change the debug setting to false.

HTML
@if (HttpContext.Current.IsDebuggingEnabled==false) { 
    <script src="~/scripts/angular2.min.js?version=@version" ></script>
 }

It's kind of amazing to think that you can bundle your entire front-end of a web application into a single file and have it download to the client. This is almost like downloading and installing a mobile application from one of the mobile application stores. Once downloaded, the only requests to your server are for RESTful service calls.

Publishing from Visual Studio 2015

One of the nice things about Visual Studio 2015 is that you can publish your application from the Publish menu option. One of my goals for this sample application was to be able to publish it to a QA or Production site from Visual Studio.

To make this happen, the gulp buildForProduction task needed to be run during the Visual Studio build/publish process. I didn't want to have to manually run the needed gulp tasks each time I published the application. Fortunately, we can run Gulp from Visual Studio 2015 automatically during the building and publishing of the application.

XML
<Target Name="Angular2 Bundle" BeforeTargets="GetProjectWebProperties">
    <Message Text="Create Angular2 Bundle from $(DestinationAppRoot)" />
    <Exec Command="call gulp buildForProduction" 
    WorkingDirectory="$(ProjectDir)" />
</Target>

Calling Gulp from Visual Studio was as simple as editing the project file and adding the above target tag. The trick was finding when Gulp can be executed during the build and publish. To figure this out, I configured Visual Studio 2015 to provide detailed information in the output window so I could see all the tasks that were executed. What I discovered was the "GetProjectWebProperties" target was executed at the beginning of the publish step (which executed right after the build step completed). Once I discovered this, I simply added the command "call gulp buildForProduction" which automatically executed from Visual Studio 2015.

Even with all the new tooling and the node.js integration during development, all I really needed to do was include the bundled angular2.min.js in the project and make a call to gulp to make the publish process completely seamless as it has always been without implementing any complicated workflow. Of course, it's not perfect because extra files (HTML templates and component JavaScript files) are published to the server that aren't needed since the application is bundled into one JavaScript file. In the future perhaps, I will improve this process. Certainly with TFS and MsBuild, you can clean up and streamline the publishing process.

Conclusion

There are a lot of things to digest when venturing into the world of Angular 2 and all the new tooling. Especially with working with a release candidate version of Angular 2. A lot of information is either scarce, scattered or becomes out-of-date quickly with breaking changes; so you'll need to dig deep to find what you are looking for. In fact, there is so much information that I had to break this article into two parts. Part 2 of this article, Developing An Angular 2 Application With TypeScript, will dig into some of the Angular 2 code for the sample application included in this article.

History

  • 13th August, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Joey Software Solutions
United States United States
Mark Caplin has specialized in Information Technology solutions for the past 30 years. Specializing in full life-cycle development projects for both enterprise-wide systems and Internet/Intranet based solutions.

For the past fifteen years, Mark has specialized in the Microsoft .NET framework using C# as his tool of choice. For the past four years Mark has been implementing Single Page Applications using the Angular platform.

When not coding, Mark enjoys playing tennis, listening to U2 music, watching Miami Dolphins football and watching movies in Blu-Ray technology.

In between all this, his wife of over 25 years, feeds him well with some great home cooked meals.

You can contact Mark at mark.caplin@gmail.com

...

Comments and Discussions

 
PraiseThank You Pin
SachinJoshi675 24-May-18 3:03
SachinJoshi675 24-May-18 3:03 
QuestionExcellent example Pin
Karay AKAR - Yapar9-Oct-17 11:15
Karay AKAR - Yapar9-Oct-17 11:15 
QuestionValidate Token needs to verify time. Pin
Karay AKAR - Yapar9-Oct-17 11:14
Karay AKAR - Yapar9-Oct-17 11:14 
QuestionFailed to load resource: net::ERR_CONNECTION_REFUSED Pin
Member 1240941823-May-17 22:05
Member 1240941823-May-17 22:05 
GeneralMy vote of 5 Pin
Karthik_Mahalingam15-Apr-17 0:27
professionalKarthik_Mahalingam15-Apr-17 0:27 
GeneralMy vote of 5 Pin
Tailslide24-Mar-17 7:50
Tailslide24-Mar-17 7:50 
This is a huge time saver having source code so well done as an example considering how complex it is to setup angular and typescript under visual studio / IIS
GeneralRe: My vote of 5 Pin
Member 1240941825-May-17 2:26
Member 1240941825-May-17 2:26 
GeneralRe: My vote of 5 Pin
Tailslide12-Apr-18 8:56
Tailslide12-Apr-18 8:56 
QuestionPackage dll could not be found Pin
pappie.paps21-Mar-17 3:20
pappie.paps21-Mar-17 3:20 
QuestionBuild issue ./../Observable' cannot be found Pin
Member 103850656-Jan-17 12:02
Member 103850656-Jan-17 12:02 
AnswerRe: Build issue ./../Observable' cannot be found Pin
pappie.paps21-Mar-17 3:21
pappie.paps21-Mar-17 3:21 
Questiontrying to update project to use lastest Angular 2 Pin
earthit18-Nov-16 3:58
earthit18-Nov-16 3:58 
QuestionAngular 2 Released Version Changes ? Pin
Member 1257086820-Sep-16 20:28
Member 1257086820-Sep-16 20:28 
QuestionEnvironment setup Pin
Member 1257086826-Aug-16 19:44
Member 1257086826-Aug-16 19:44 
AnswerRe: Environment setup Pin
Mark J. Caplin27-Aug-16 1:45
Mark J. Caplin27-Aug-16 1:45 
GeneralRe: Environment setup Pin
Member 1257086827-Aug-16 6:25
Member 1257086827-Aug-16 6:25 
QuestionThanks a lot! Pin
Irene Troupansky17-Aug-16 5:42
Irene Troupansky17-Aug-16 5:42 
QuestionIssue with the source code provided Pin
razen_smiles17-Aug-16 0:02
razen_smiles17-Aug-16 0:02 
AnswerRe: Issue with the source code provided Pin
K. Zimny28-Nov-16 4:50
K. Zimny28-Nov-16 4:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.