Your First Angular 2, ASP.NET Core Project in Visual Studio Code – Part 3

Angular2, ASP.NET Core, Visual Studio Code, Part 3

Derived from photo by Markus Spiske / raumrot.com, CC-BY

This is part three in a series of posts teaching you how to create your first Angular 2 application with ASP.NET Core in Visual Studio Code. Here is the list of posts:

In this post, you configure the Angular 2 dependencies. There is a lot of material to cover. Much of this configuration has been taken directly from the Angular 2 Quick Start as it has been proven to work. There are some exceptions, though, in order to accomodate the usage of ASP.NET Core as the server-side framework instead of NodeJS.

If you would like to jump straight to the code, it's all on GitHub.

NPM Dependencies

Angular 2 and its dependencies are delivered through npm. Create a package.json file in your application directory with the following configuration:

{
  "name": "angular2-aspnetcore-starter",
  "version": "0.0.1",
  "description": "Starter application using Angular 2 and ASP.NET Core",
  "scripts": {

  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@angular/common": "~2.2.0",
    "@angular/compiler": "~2.2.0",
    "@angular/core": "~2.2.0",
    "@angular/forms": "~2.2.0",
    "@angular/http": "~2.2.0",
    "@angular/platform-browser": "~2.2.0",
    "@angular/platform-browser-dynamic": "~2.2.0",
    "@angular/router": "~3.2.0",

    "angular-in-memory-web-api": "~0.1.15",
    "systemjs": "0.19.40",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "5.0.0-beta.12",
    "zone.js": "^0.6.26"
  },
  "devDependencies": {
    "concurrently": "^3.1.0",
    "typescript": "^2.0.10",
    "@types/node": "^6.0.46"
  },
  "repository": { }
}

Update: The original version of this post omitted the @types/node package from devDependencies. This typings library should be added to your project as it contains definitions for the module.id API used in the next part of this series.

This file contains metadata for your application but most importantly it lists all of the dependencies and devDependencies. The dependencies are the libraries that your application uses at runtime. The devDependencies are libraries you use during your build.

In this project, you use TypeScript which requires a compilation/build step to convert the TypeScript code to JavaScript that the browser understands. In your devDependencies, you see typescript and concurrently listed. The concurrently library is discussed later.

As for the libraries listed under dependencies, most of these are Angular libraries. Notice how there are 9 libraries with some form of 'angular' in their name. The Angular team was deliberate in breaking down the framework so that you can reference only those pieces you need. For this example, all of them are included but you won't use them all. They are listed for reference.

There are five other runtime dependencies:

SystemJS

SystemJS is a configurable module loader. Read more about it in the dedicated section below.

core-js

This is a shim providing polyfill implementations for common ECMAScript 2015 (and beyond) features. If you aren't familiar with the concept of a shim, it's a piece of code that implements language features not natively supported by the browser. There is a list of provided features here.

reflect-metadata

This is another shim, providing support for ECMAScript decorators. Decorators are similar to annotations in C# and allow you to add metadata to JavaScript objects. Angular 2 heavily uses decorators in the framework. This is an example of the Component decorator:

@Component({
    selector: 'my-component',
})
export class MyComponent{ }

For reference, check out the reflect-metadata documentation on GitHub.

rxjs

Another shim for observables. In certain parts of Angular, especially @angular/http observables are a first-class citizen. You won't be using them in this getting started series but you should start learning about them. This 7-minute video is an excellent place to start.

zone.js

There is no use trying to explain it briefly. You don't need to understand it for now but if you're curious, go head and watch this video.

Now that your package.json file is in place, install the packages using Visual Studio Code's terminal (Ctrl+`) and type the command npm install.

TypeScript Configuration

TypeScript is arguably the best-supported language in Angular 2. It seems to be a hit in the Angular community. Also if you're used to C#, you will feel at home with having types in your JavaScript. (Although, there are significant differences between the C# and TypeScript type systems as you will come to find).

To configure the TypeScript compilation, you add a tsconfig.json file to the root of your application. This is the recommended configuration:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [ "es2015", "dom" ],
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  }
}

Along with types, TypeScript gives you the capability to use ECMAScript features that have not yet been added to the browsers. In order to target the broadest range of browser compatibility today, the target property with the value es5 tells the TypeScript compiler to emit ECMAScript 5 compatible JavaScript.

The project configuration uses node module resolution. Both the module and moduleResolution properties handle this. You can read more about TypeScript module resolution on this site or visit the TypeScript documentation.

The emitDecoratorMetadata and experimentalDecorators properties enable compilation of the ECMAScript decorator syntax that Angular 2 utilizes. And finally the lib property configuration provides types for standard ECMAScript 2015 language features and DOM (Document Object Model) APIs.

SystemJS Configuration

The SystemJS library has two responsibilities. The first is to act as a module loader. At the time of writing, today's browsers don't yet support ECMAScript 2015 modules. SystemJS acts as a shim for dynamic module loading.

Its other responsibility is to map node-based import statements to relative URLs – essentially mimicking npm-based module resolution in the browser. There is a post on this site that goes into more detail about the module-loading capabilities of SystemJS. You can also visit the documentation here.

Node module resolution allows you to import modules by package name:

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

However, the browser doesn't understand this. Therefore, for each npm-based library you use in the browser, you should have corresponding configuration telling SystemJS from where to load the scripts.

In your application, create a file named systemjs.config.js. The configuration looks like this (again, courtery of the Angular Quick Start):

/**
 * 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: 'app',

      // 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',
      'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
    },
    // 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);

In particular, look in the configuration at the package called app that points to a main file, ./main.js. This is used in the next section.

Update index.html

Now that you have the tooling downloaded and configured, it's time to reference the shims, loaders, and libraries in your index.html file:

<!DOCTYPE html>
<html>
  <head>
    <title>Angular 2, ASP.NET Core Starter</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Polyfill(s) for older browsers -->
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>

    <script src="systemjs.config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
  </body>
</html>

Remember the ./main.js path in the SystemJS configuration? Now look a the System.import('app')... statement in the HTML:

System.import('app').catch(function(err){ console.error(err); });

This statement uses SystemJS to import the app module, defined in the SystemJS configuration, to request app/main.js from the server. This file does not exist yet.

Start Me Up

Instead of creating app/main.js, create the app directory at the root of your application and then create a main.ts TypeScript file within the app folder.

Application Structure

Use this as the contents of the main.ts file:

document.body.innerHTML = 'Hello, TypeScript World!';

Now it's time to run the application. However, instead of typing a bunch of commands in the terminal, create an npm script.

Remember all the way back at the beginning, you installed the concurrently library from npm? This library allows you to run several commands in the terminal, umm… concurrently. Add this script to your package.json file:

"scripts" : {
    "start": "tsc && concurrently \"tsc -w\" \"dotnet run\" "
},

Now in the terminal, type npm start to compile the TypeScript and start the ASP.NET Core application. Furthermore, tsc -w runs the TypeScript compiler in watch mode so whenever you save a modified TypeScript file, the compiler regenerates the JavaScript.

Now open your browser to http://localhost:5000 and see the Hello, TypeScript World! message. In a future post, you further automate this configuration by incorporating Visual Studio Code key bindings and Browser Link.

Hello, TypeScript World!

Conclusion

Configuring the Angular 2 dependencies isn't trivial. However, understanding how these systems work will save you countless hours diagnosing issues. The modularity of npm is powerful but also comes with complexity. Now, you have a better understanding of what is happening behind the scenes of your Angular 2 application and are better equipped for your future development.

In the next post, you build on this foundation and actually write some Angular code. In the comments below, please discuss your adventures in npm configuration. There are so many options out there so if you have any tips, please leave a comment.

5 thoughts on “Your First Angular 2, ASP.NET Core Project in Visual Studio Code – Part 3”

  1. I follow your post and reach the “npm start”, but it raised error of “tsc” not found. It seems the typescript is not installed when doing “npm install”. I run “npm install -g typescript” to manually install it and then it works fine. Don’t know why it happens…

  2. Doesnt show anything after running npm start. I then check if all files are loaded and shim.min.js is not existing, and others are showing this in console:

    SyntaxError: missing ; before statement[Learn More] shim.js:1:6
    17:28:03.543 SyntaxError: missing ; before statement[Learn More] zone.js:1:6
    17:28:03.545 SyntaxError: missing ; before statement[Learn More] Reflect.js:1:6
    17:28:03.547 SyntaxError: missing ; before statement[Learn More] system.src.js:1:6
    17:28:03.548 SyntaxError: missing ; before statement[Learn More] systemjs.config.js:1:6
    17:28:03.549 ReferenceError: System is not defined[Learn More] localhost:5000:17:7

  3. so here in screenshot you only have Startup.cs and in previous program.cs as well. I can’t go through part where we run npm start to show Hello TypeScript world.

Leave a Reply

Your email address will not be published. Required fields are marked *