ASP.NET MVC 5 – Using Angular 2 with TypeScript in Visual Studio

Datetime:2016-08-23 04:38:14          Topic: AngularJS  Visual Studio  ASP.NET MVC           Share

ASP.NET MVC 5 is widely used web development framework, it’s stable, matured and most importantly its is used in production on large scale. Many folks had requested me to write how to wire up Angular 2 in MVC 5.

These steps can be used for new or existing MVC 5 application.

In this post I will summarize the steps needed to getting started with Angular 2 in MVC 5.

ASP.NET MVC 5 is full .NET framework web development framework, it’s different than ASP.NET Core 1.0

What we will learn?

  1. Adding package.json to MVC 5
  2. Configure to transpile TypeScript files
  3. Using gulpfile.js to move files.
  4. Add TypeScript files for bootstrapping
  5. Include systemjs.config.js to load Angular 2 modules
  6. Change HTML to load and render Angular

Step 1: Adding package.json to ASP.NET MVC 5

Assuming that you already have existing or creating new ASP.NET MVC 5. Lets add NPM configuration file known as package.json . It contains Angular 2 & related packages name to installed using NPM (Node). This is similar to package.config of NuGet.

Latest NodeJS & NPM needs to be installed.

{
  "version": "1.0.0",
  "name": "aspnet",
  "private": true,
  "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.1",
    "@angular/router-deprecated": "2.0.0-rc.2",
    "@angular/upgrade": "2.0.0-rc.4",
    "@angular2-material/button": "^2.0.0-alpha.6",
    "@angular2-material/card": "^2.0.0-alpha.6",
    "@angular2-material/checkbox": "^2.0.0-alpha.6",
    "@angular2-material/core": "^2.0.0-alpha.6",
    "@angular2-material/grid-list": "^2.0.0-alpha.6",
    "@angular2-material/icon": "^2.0.0-alpha.6",
    "@angular2-material/input": "^2.0.0-alpha.6",
    "@angular2-material/list": "^2.0.0-alpha.6",
    "@angular2-material/progress-bar": "^2.0.0-alpha.6",
    "@angular2-material/progress-circle": "^2.0.0-alpha.6",
    "@angular2-material/radio": "^2.0.0-alpha.6",
    "@angular2-material/sidenav": "^2.0.0-alpha.6",
    "@angular2-material/tabs": "^2.0.0-alpha.6",
    "@angular2-material/toolbar": "^2.0.0-alpha.6",
    "angular2-in-memory-web-api": "0.0.14",
    "core-js": "^2.4.0",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "systemjs": "0.19.27",
    "zone.js": "^0.6.12"
  },
  "devDependencies": {
    "typescript": "^1.8.10",
    "gulp": "^3.9.1",
    "path": "^0.12.7",
    "gulp-clean": "^0.3.2",
    "gulp-concat": "^2.6.0",
    "gulp-typescript": "^2.13.6",
    "typings": "^1.3.1",
    "gulp-tsc": "^1.2.0"
  }
}

package.json contains Angular 2 along with Angular Material, system.js, RxJs and also some dev dependencies.

Open Command Prompt & navigate to package.json location, then run npm install this will install packages related to Angular 2 under node_modules folder in your folder structure.

They won’t be showing in project solution explorer, don’t worry they need not show.

Step 2: Configure to transpile TypeScript files

TypeScript(TS) would be something new for most of developers, maybe these will give get started on TypeScript

In short – It’s superset of JavaScript, means everything you know about JS will be in use.

All TS files need to be transpiled or compiled to JS files so that we can run them on browser. To accomplish this we need to add “TypeScript Configuration File” called as tsconfig.json

Create a folder called “ tsScripts ” which contains all TS files and also configuration file. Create above tsconfig.json in this folder.

{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "commonjs",
    "noEmitOnError": true,
    "noImplicitAny": false,
    "outDir": "../Scripts/",
    "removeComments": false,
    "sourceMap": true,
    "target": "es5",
    "moduleResolution": "node"
  },
  "exclude": [
    "node_modules",
    "typings/index",
    "typings/index.d.ts"
  ]
}

It’s fairly simple config “All TS files present in tsScripts folder will be transpiled using commonjs module to outDir (Output Directory) by keeping comments, sourceMap intact”

One of important steps is to create typings.json file, this file will create typings to ensure that TypeScript understands all Angular 2 modules in respect to ES5 standard.

Create JSON file with name “ typings.json ” & add below code then run command typings install from CMD

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

Step 3: Using gulpfile.js to move files

From Step 1 you got to know that all Angular 2, Material packages are downloaded into node_modules in your solution folder. Now we need to move required files only like JS, sourcemaps (debugging on chrome) into our MVC 5 apps Scripts folder.

Step 2 also requires to move TS files to JavaScript file, so we use create GULP tasks which does this transpile. Also it contains CSS files movement also (which is not relevant at this point)

Create gulpfile.js in your project and copy this code

var ts = require('gulp-typescript');
var gulp = require('gulp');
var clean = require('gulp-clean');
 
var destPath = './libs/';
 
// Delete the dist directory
gulp.task('clean', function () {
    return gulp.src(destPath)
        .pipe(clean());
});
 
gulp.task("scriptsNStyles", () => {
    gulp.src([
            'core-js/client/**',
            'systemjs/dist/system.src.js',
            'reflect-metadata/**',
            'rxjs/**',
            'zone.js/dist/**',
            '@angular/**',
            'jquery/dist/jquery.*js',
            'bootstrap/dist/js/bootstrap.*js',
            '@angular2-material/**/*',
    ], {
        cwd: "node_modules/**"
    })
        .pipe(gulp.dest("./libs"));
});
 
var tsProject = ts.createProject('tsScripts/tsconfig.json');
gulp.task('ts', function (done) {
    //var tsResult = tsProject.src()
    var tsResult = gulp.src([
            "tsScripts/*.ts"
    ])
        .pipe(ts(tsProject), undefined, ts.reporter.fullReporter());
    return tsResult.js.pipe(gulp.dest('./Scripts'));
});
 
gulp.task('watch', ['watch.ts']);
 
gulp.task('watch.ts', ['ts'], function () {
    return gulp.watch('tsScripts/*.ts', ['ts']);
});
 
gulp.task('default', ['scriptsNStyles', 'watch']);

Step 4: Add TypeScript files for bootstrapping

As we are using Angular 2 with TypeScript, we need add TS file to bootstrap the Angular app into MVC 5 app. So lets create boot.ts file in “TsScripts” folder and copy this code

///<reference path="./../typings/globals/core-js/index.d.ts"/>
import {bootstrap}    from '@angular/platform-browser-dynamic';
import {AppComponent} from './app';
 
bootstrap(AppComponent);

AppComponent is starter component which we have to create it now (app.ts). Add this code below

import {Component} from '@angular/core';
import { MD_LIST_DIRECTIVES } from '@angular2-material/list';
@Component({
    selector: 'my-app',
    template: `<md-list>
  <md-list-item> MVC 5 </md-list-item>
  <md-list-item> Angular 2 </md-list-item>
  <md-list-item> VisualStudio 2015 </md-list-item>
</md-list>`,
    directives: [
        MD_LIST_DIRECTIVES,
    ],
})
export class AppComponent { }

This is template based app component which displays list in Angular Material style.

Step 5: Include systemjs.config.js to load modules

This is most important part of Angular 2 which loads it into browser. There are different ways to load it but am using SystemJS here.

In the existing “ Scripts ” folder, create “ systemjs.config.js ” and copy below code

/**
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function (global) {
    // map tells the System loader where to look for things
    var map = {
        'app': 'Scripts', // 'dist',
        '@angular': './libs/@angular',
        'angular2-in-memory-web-api': './libs/angular2-in-memory-web-api',
        'rxjs': './libs/rxjs'        
    };
 
    var Materialmap = {        
        '@angular2-material': './libs/@angular2-material'
    };
 
 
    // packages tells the System loader how to load when no filename and/or no extension
    var packages = {
        'app': { main: 'main.js', defaultExtension: 'js' },
        'rxjs': { defaultExtension: 'js' },
        'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },        
    };
    // packages tells the System loader how to load when no filename and/or no extension
    var materialpackages = {        
        '@angular2-material/core': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'core.js'
        },
        '@angular2-material/sidenav': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'sidenav.js'
        },
        '@angular2-material/toolbar': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'toolbar.js'
        },
        '@angular2-material/card': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'card.js'
        },
        '@angular2-material/button': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'button.js'
        },
        '@angular2-material/checkbox': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'checkbox.js'
        },
        '@angular2-material/radio': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'radio.js'
        },
        '@angular2-material/progress-circle': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'progress-circle.js'
        },
        '@angular2-material/progress-bar': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'progress-bar.js'
        },
        '@angular2-material/input': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'input.js'
        },
        '@angular2-material/list': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'list.js'
        },
        '@angular2-material/icon': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'icon.js'
        },
        '@angular2-material/tabs': {
            format: 'cjs',
            defaultExtension: 'js',
            main: 'tabs.js'
        },
    };
    var ngPackageNames = [
      'common',
      'compiler',
      'core',
      'forms',
      'http',
      'platform-browser',
      'platform-browser-dynamic',
      'router',
      'router-deprecated',
      'upgrade'      
    ];
    var ngMaterialPackageNames = [      
      'core',
      'sidenav',
      'toolbar',
      'card',
      'button',
      'checkbox',
      'radio',
      'progress-circle',
      'progress-bar',
      'input',
      'list',
      'icon',
      'tabs'
    ];
    // Individual files (~300 requests):
    function packIndex(pkgName) {
        packages['@angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' };        
    }
 
    function MpackIndex(pkgName) {
        materialpackages['@angular2-material'+ pkgName] = { main:  pkgName + '.js', defaultExtension: 'js' };        
    }
 
    // Bundled (~40 requests):
    function packUmd(pkgName) {
        packages['@angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };        
    }
 
    // Bundled (~40 requests):
    function MpackUmd(pkgName) {
        packages['@angular2-material/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };        
    }
 
    // Most environments should use UMD; some (Karma) need the individual index files
    var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
    // Add package entries for angular packages
    ngPackageNames.forEach(setPackageConfig);
    var config = {
        map: map,
        packages: packages
    };
    System.config(config);
 
    // Most environments should use UMD; some (Karma) need the individual index files
    var setMaterialsPackageConfig = System.packageWithIndex ? MpackIndex : MpackUmd;
    // Add package entries for angular packages
    ngMaterialPackageNames.forEach(setMaterialsPackageConfig);
    var Materialconfig = {
        map: Materialmap,
        packages: materialpackages
    };
    System.config(Materialconfig);
    
})(this);

Step 6: Change csHTML to load and render Angular 2

To load Angular 2 in ASP.NET MVC 5, we need to include the script references in _Layout file and index.cshtml page.

<!DOCTYPEhtml>
<html>
<head>
    <metacharset="utf-8" />
    <metaname="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - MyASP.NETApplication</title>
 
    <!-- 1. Loadlibraries -->
    <!-- Polyfill(s) for older browsers -->
 
    <scriptsrc="~/libs/core-js/client/shim.min.js"></script>
    <scriptsrc="~/libs/zone.js/dist/zone.js"></script>
    <scriptsrc="~/libs/reflect-metadata/Reflect.js"></script>
    <scriptsrc="~/libs/systemjs/dist/system.src.js"></script>
 
    <!-- 2. Configure SystemJS -->
    <scriptsrc="~/Scripts/systemjs.config.js"></script>
    <script>
        System.import('../Scripts/boot').catch(function (err)
        {
            console.error(err);
        });
    </script>
 
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <divclass="navbar navbar-inverse navbar-fixed-top">
        <divclass="container">
            <divclass="navbar-header">
                <buttontype="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <spanclass="icon-bar"></span>
                    <spanclass="icon-bar"></span>
                    <spanclass="icon-bar"></span>
                </button>
                @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <divclass="navbar-collapse collapse">
                <ulclass="nav navbar-nav">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                </ul>
            </div>
        </div>
    </div>
    <divclass="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>© @DateTime.Now.Year - MyASP.NETApplication</p>
        </footer>
    </div>
 
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

In the Views/Home/index.cshtml you need to add “ my-app ” component we defined in app.TS file. This is starting point of Angular 2 application to render into browser.

@{
    ViewBag.Title = "Home Page";
}
 
<my-app>Loading...</my-app>

Now that we are almost done, we need to run GULP tasks so that Angular 2 files, TS files are moved to appropriate folder. Open Task Runner Explorer in Visual Studio 2015 and run default task shown.

Its better to do show ALL Files in solution Explorer to see “ libs “, “ typings “, “ app.js, boot.js, *.map ” files. Includes these files and run application to load Angular 2 in ASP.NET MVC 5.

Angular 2 running on ASP.NET MVC 5

It’s best to have latest NodeJS, TypeScript installed to make it smooth process.

I have entire source code is on my Github repo , clone or fork or download it and follow instructions to run it.

Let me know in comments if you face any issues running applications.





About List