Preparing the workspace

The first thing we have to do is to set up our workstation: it won't be difficult because we only need a small set of essential tools. These include Visual Studio 2015, the web platform installer, .NET Framework 4.5 or higher, a web server such as IIS or IIS Express, and a decent source code control system such as Git, Mercurial, or Team Foundation, which we will take for granted as you most likely already have them in place. Just make sure you're all set with these before going further.

Note

IMPORTANT

If you haven't already, be sure to download and install Visual Studio 2015 Update 3 and the .NET Core for Visual Studio Tools Preview 2. These are the latest updates at the time of writing, but will be updated in the near future.

These updates will address some important issues for web development tools, as well as adding improved support for TypeScript, NPM, and other components that we'll be using later on:

Setting up the project

The first thing we need to do is to create a new ASP.NET Core web application project:

  1. Fire up Visual Studio 2015 and, from the File menu, expand New and select Project to open a new project modal window.
  2. From the Templates tree, expand the Visual C# node and select the Web subfolder: the right section of the modal window will be populated by a number of available project templates. Among these, there are two choices for creating an ASP.NET Core web application project: the first one, optimized for cross-platform deployment, entirely relies upon the new .NET Core Framework; the other one, ideal for a Windows environment, is based upon the latest .NET Framework version (4.6.1 at the time of writing).

    Setting up the project

  3. The good thing here is that, thanks to the ASP.NET Core versatility, we are free to choose the approach we like the most, as both frameworks are mature enough to support almost everything we will use within this book. The only relevant downside of the .NET Core choice is the lack of compatibility with some NuGet packages that haven't been ported there yet: that's why, at least for our example, we'll be choosing to stick with the full-scale .NET Framework. In order to do that, select the ASP.NET Core Web Application (.NET Framework) template and fill in the relevant Name, Location, and Solution name fields. We'll name the solution OpenGameList, while the project will be called OpenGameListWebApp,as shown in the previous screenshot. Once done, click OK to continue.
  4. In the next modal window, we can further customize our template by choosing the default contents to include in our project (Empty, Web API, or Web Application) and the authentication mechanism, should we want to use one. Choose Web API and No authentication, then click the OK button to create the project.

    Setting up the project

  5. If you're used to the Visual Studio Web Application Project templates from previous ASP.NET versions you may be tempted to choose Empty instead, thus avoiding the insane amount of sample classes, folders, and components, including a number of potentially outdated versions of various client-side frameworks such as Bootstrap, KnockoutJS, JQuery, and more. Luckily, these new ASP.NET Core project templates are extremely lightweight - the one we did choose comes with nothing more than a /Controller/ folder, a ValuesController.cs sample, and some useful references in the Startup.cs file.

Package and resource managers

Now that our project has been created, it's time to add the resources we're going to use. As we already mentioned, the layout of a standard ASP.NET Core solution is quite different from what it used to be. The main differences are:

  • The solution's projects are now created in a /src/ folder by default. This is just a convention, though, as they can be placed anywhere.
  • There is a brand-new wwwroot folder, which will contain the compiled, ready-to-publish contents of our application, while everything else will be the project source code.

Other things worth noting are a Controller folder, containing a sample ValueController.cs class, a Startup.cs file containing the application class and a couple of other files we'll address in a while.

Package and resource managers

The first thing we need to do is to add a folder called Scripts to the root of our project. We'll use this folder to place all of our JavaScript files, and then we'll combine and minify these using a JavaScript Task Runner such as Gulp, this way we'll make sure that the resulting file will be added to the wwwroot folder automatically after each build.

Installing the packages

Now it's time to make a good use of the three package managers natively supported by ASP.NET, namely NuGet, NPM, and Bower. These tools will allow you to gather all the resources you need to build your application: they will download all the resources and their dependencies automatically, so you needn't do it manually, thus saving a lot of time. In case you're wondering why we need three of them, it can be useful to briefly recap their scope:

  • NuGet: This will take care of all the .NET native and third-party packages such as Entity Framework, ASP.NET MVC, and so on. The full package list is stored in the project.json file so they can be retrieved and/or checked for updates at any time.
  • NPM: This was the default package manager for the JavaScript runtime environment known as Node.js. Over the last few years, though, it has also been used to host a number of projects, libraries, and frameworks of any kind, including Angular 2. The package list is stored in the package.json file.
  • Bower: Another package management system for client-side programming, created by Twitter and maintained on GitHub, specifically designed for frontend development such as jQuery, Bootstrap, and AngularJS. It depends on Node.js and NPM and works under git. Its configuration file is called bower.json. Notice that, since the Angular 2 team is pushing their code using NPM rather than Bower, we won't be using it in our project.

NuGet and ASP.NET

ASP.NET Core gives us at least four different ways to add NuGet packages to our project:

  • Using the Visual Studio powered GUI, accessible by right-clicking the project and choosing Manage NuGet Packages.
  • Using the Package Manager Console, with the well-renowned Install-Package command followed by the package name and build version.
  • Using the on-screen helper tools provided by Intellisense, limited to the native .NET modules/libraries.
  • Directly adding the package reference to the project's NPM configuration file, also known as project.json.

The first three methods, although being absolutely viable, are basically shortcuts for populating the fourth one; the latter has the advantage of being the less opaque one, so we'll just use it.

Project.json

Open the project.json file, find the dependencies section and add the following packages to the list (new lines are highlighted):

  "dependencies": { 
    "Microsoft.AspNetCore.Mvc": "1.0.0", 
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", 
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", 
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", 
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", 
    "Microsoft.Extensions.Configuration.Json": "1.0.0", 
    "Microsoft.Extensions.Logging": "1.0.0", 
    "Microsoft.Extensions.Logging.Console": "1.0.0", 
    "Microsoft.Extensions.Logging.Debug": "1.0.0", 
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", 
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",

    "Microsoft.AspNetCore.Routing": "1.0.0",

    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",

    "Microsoft.AspNetCore.StaticFiles": "1.0.0",

    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0"
  }

We have added a space to visually separate the default dependencies required by all ASP.NET Core projects from our newly added ones.

Tip

The listed builds are the latest at the time of writing, but they won't last forever: as soon as ASP.NET Core passes its initial release (1.0.0 at the time of writing), these numbers will gradually increase over time, whenever a new version comes out. To check the latest version of each package, just place the cursor between the quotes and delete the version number, a dynamic drop-down list will be shown containing all the latest versions for that given module.

While we're here, it can be useful to check which version of the .NET Framework we are targeting by looking at the frameworks key. Since we choose the .NET Framework template, we should find something like this:

  "frameworks": { 
    "net461": { } 
  }, 

This will most likely change in the future, so be sure to target a version compatible with the packages you want to use. For the purpose of this book, the .NET Framework 4.6.1 will do the job.

As soon as we save the project.json file, Visual Studio will start to retrieve the missing packages from the web. Wait for it to finish, then proceed with opening the Startup.cs file, which is also in the project root.

Startup.cs

If you're a seasoned .NET developer you might already be familiar with the Startup.cs file, introduced in OWIN-based applications to replace most of the tasks previously handled by the good old Global.asax file. In ASP.NET Core, the Startup.cs file serves the same purpose as the OWIN startup class, being nothing less than the application main entry point, it is the place where we can add services, choose which application modules and middleware functions to load, handle dependency injection tasks, and configure the pipeline.

However, the similarities end here, the class has been completely rewritten to be as pluggable and lightweight as possible, meaning that it will include and load only what's strictly necessary to fulfill our application's tasks.

To better understand this, let's take a look at the following lines taken from the Startup.cs source code shipped with the ASP.NET Core Web API project template we chose:

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
{ 
    loggerFactory.AddConsole(Configuration.GetSection("Logging")); 
    loggerFactory.AddDebug(); 
 
    app.UseMvc(); 
} 

Notice how empty our application's HTTP request pipeline is, it won't ever serve static files, since there is nothing telling it to do so. To better understand it, let's perform a quick test before proceeding.

Testing the HTTP request pipeline

In order to check that the ASP.NET pipeline is properly working, click on the Start Debugging option in the Debug menu, or just hit the F5 hotkey. If everything is OK, your default web browser will open pointing to the following URL: http://localhost:14600/api/values.

The page content will show the following:

Testing the HTTP request pipeline

If we're seeing the preceding screenshot, it means that the request pipeline is working fine, the MVC module has been added within the Startup.cs file and it's working as expected. That's because there's a sample ValueController.cs class in the /Controllers/ folder, conveniently added by the Web API project template we chose a while ago, that behaves exactly like this.

Now let's try to request the static Project_Readme.html file, also added by our chosen template in the project root. In order to reach it, we need to move it inside the /wwwroot/ folder. Once done, it should be reachable by pointing at the following URL: http://localhost:14600/Project_Readme.html.

However, if we try to do that, and then issue that request using the same browser we used before, we would get the following response:

Testing the HTTP request pipeline

This HTTP 404 error clearly demonstrates what we've just said, the HTTP request pipeline won't serve static files, simply because we didn't tell it to. However, we can easily fix that behavior by adding them to the pipeline within the Startup.cs file (new lines highlighted):

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
{ 
    loggerFactory.AddConsole(Configuration.GetSection("Logging")); 
    loggerFactory.AddDebug(); 
 
    // Configure a rewrite rule to auto-lookup for standard default files such as index.html.
    app.UseDefaultFiles();

    // Serve static files (html, css, js, images & more). See also the following URL:

    // https://docs.asp.net/en/latest/fundamentals/static-files.html for further reference.

    app.UseStaticFiles();

    // Add MVC to the pipeline 
    app.UseMvc(); 
} 

While we are here, we also added the following:

  • A rewrite rule to enable support for the most common default files (such as index.htm, index.html, and so on), which will be automatically served without the user having to fully qualify the URI.
  • A series of comments to better clarify the meaning of each module, including a reference link to the official ASP.NET Core documentation.

If we run our application again, we should now be welcomed with the following:

Testing the HTTP request pipeline

That's it. We have enabled static file support, so that we'll be able to serve not only HTML but also CSS, JS files, and so on. Delete the Project_Readme.html and get ready to install an important third-party NuGet package that we'll be using a lot in the following chapters.

Newtonsoft.Json

If you're working with ASP.NET and you've never heard about Newtonsoft.Json (formerly Json.NET), you've almost certainly missed something that could've eased your job, big time. We're talking about one of the finest libraries - and most useful tools, ever developed for .NET, at least for the writer. It's a very efficient (and thus very popular), high-performance JSON serializer, deserializer, and all-around framework for .NET, which also happens to be completely open source.

We won't dig into it anymore here, as we'll be using it soon enough. For now, let's just install it by right-clicking on our solution's node in the Solution Explorer, then choosing Manage NuGet Packages for this Solution... to open up the following panel:

Newtonsoft.Json

Search for Newtonsoft.Json to make it appear if it isn't there already, then left-click to select it. Ensure to select the latest stable version (9.0.1 at the time of writing), click on Install, and then wait for the installer to complete its job.

That's it for now: if we open the project.json file now, we can see that the Newtonsoft.Json package reference has been added at the end of the dependencies section, together with the other project-specific dependencies we've manually added before. We'll be installing other packages using the GUI in the following chapters, as soon as we need them, now that we know how easy it is to do that.

JavaScript, TypeScript, or Dart?

Now it's time to choose the client programming language to adopt. Given the fact we're planning to use Angular 2, our choices are basically the following three: good old JavaScript, its Microsoft superset known as TypeScript, or the Google growing beast known as Dart.

In this project, we're going to use TypeScript for a number of good reasons, the most important of them are as follows:

  • TypeScript has a number of features that JavaScript doesn't, such as static typing, classes, and interfaces. Using it in Visual Studio also gives us the chance to benefit from the built-in IntelliSense, which, together with its distinctive features, will allow us to spot most programming errors as we type the code, potentially saving a great amount of time.
  • For a large client-side project, TypeScript will allow us to produce a more robust code, which will also be fully deployable anywhere a plain JavaScript file would run. As a matter of fact, since TypeScript is a superset of JavaScript it can be used alongside any JavaScript code without problems.
  • Dart is a wonderful newcomer, and it will probably surpass its ECMA script-rivals soon. Currently though, it is still quite immature in terms of available third-party libraries, documentation, development support, and overall community knowledge.

We're not the only ones praising TypeScript: it's something acknowledged by the Angular team itself, considering the fact that the Angular 2 source code has been written using TypeScript, as proudly announced by Microsoft in the following MDSN blog post:  https://blogs.msdn.microsoft.com/typescript/2015/03/05/angular-2-built-on-typescript/.

Adding the tsconfig.json file

The first thing we hav to do to set up TypeScript is to add a tsconfig.json file to our root project.

In the e to do to set up TypeScript is to add a tsconfig.json file to our root project. In the Solution Explorer, right-click on the root project node and select Add, then New Item; switch to the client-side from the top left tree view, then select TypeScript Configuration File, and add the tsconfig.json file to the project root.

Adding the tsconfig.json file

The tsconfig.json file will be generated with its default set of options, which are good for most scenarios but not ideal for what we're going to do. That's why we need to add/change some settings until it will look like the following:

{ 
  "compileOnSave": false, 
  "compilerOptions": { 
    "emitDecoratorMetadata": true, 
    "experimentalDecorators": true, 
    "module": "system", 
    "moduleResolution": "node", 
    "noImplicitAny": false, 
    "noEmitOnError": false, 
    "removeComments": false, 
    "target": "es5" 
  }, 
  "exclude": [ 
    "node_modules", 
    "wwwroot" 
  ] 
} 

These instructions will influence how Intellisense and our external TypeScript compiler will work; two things that will help us, big time.

Wait a minute, did we just say external TypeScript compiler? What about the built-in, integrated TypeScript compile feature provided by the Visual Studio IDE?

Well, we just said that one of TypeScript's biggest advantages is that we can use it anywhere a plain JavaScript file would run, that's because any .ts file can be compiled into a .js file without any problem. The compile task is handled by the TypeScript compiler itself, also known as tsc, which can be run directly from the Visual Studio IDE with the help of the tsconfig.json we added in the preceding paragraph. That file, as we've just seen, contains a wide set of compiling options such as creating source maps, creating definition files, concatenating everything into a single output file, and so on. Looking great, isn't it?

Unfortunately, not so much. Although Visual Studio 2015 does a pretty decent job of acting as a bridge between our TypeScript sources and tsc, it still lacks an important key feature, it doesn't give the option to minify/uglify the resulting JS files, thus leaving them uncompressed after the compile task.

Such feature can be trivial during development, yet it happens to be quite important in production because we'll definitely want to cloak our source code before publishing it. That's why the best thing we can do is to stop the Visual Studio IDE from compiling TypeScript files and implement a custom alternative that does support minify/uglify.

Note

In case you're wondering about what the terms minify and/or uglify actually mean, we strongly suggest that you read the following Wikipedia page:  https://en.wikipedia.org/wiki/Minification_(programming).

Luckily, we're going to use something we would need anyway, a dedicated, streamlined, and modern task runner that goes by the name of Gulp. In order to install it, though, we need to set up the appropriate package manager.

Introducing NPM

NPM is the tool we will use to add some important packages to our project, the most relevant ones being Gulp and Angular 2. To install it, do the following:

  1. Go to the Solution Explorer.
  2. Right-click on the root project node.
  3. Select Add, then New Item.
  4. Switch to Client-side from the top left tree view, then select NPM Configuration File, and add the package.json file to the project root.

Introducing NPM

Change the contents of the automatically-generated package.json file to match the following:

{ 
  "version": "1.0.0", 
  "name": "opengamelistwebapp", 
  "private": true, 
  "dependencies": { 
  }, 
  "devDependencies": { 
    "gulp": "^3.9.1", 
    "gulp-clean": "^0.3.2", 
    "gulp-concat": "^2.6.0", 
    "gulp-sourcemaps": "^1.6.0", 
    "gulp-typescript": "^2.13.6", 
    "gulp-uglify": "^2.0.0", 
    "typescript": "^1.8.10" 
  } 
} 

Please notice that, at the time of writing, Gulp 3.9.1 is the latest version. You can check the most recent build by checking the Visual Studio Intellisense, which is able to perform impressive real-time update checks upon each package's latest build just like it does within the project.json file:

Introducing NPM

As an alternative, you can always visit the Gulp project NPM page at https://www.npmjs.com/package/gulp and change the build version numbers accordingly.

Notice that you aren't forced to input precise build numbers, as you can also use the standard npmjs syntax to specify auto-update rules bound to custom version ranges using the supported prefixes, such as the following:

  • The Tilde (~): ~1.1.4 will match all 1.1.x versions, excluding 1.2.0, 1.0.x and so on.
  • The Caret (^): ^1.1.4 will match everything above 1.1.4, excluding 2.0.0 and above.

This is another scenario where Intellisense will come in handy, as it will also suggest how to do it.

Note

For an extensive list of available npmjs commands and prefixes, you can also check out the official npmjs documentation at https://docs.npmjs.com/files/package.json.

Working with Gulp

As you most likely already know, Gulp is a powerful task runner toolkit that we will use to automate some time-consuming tasks in our development workflow. If you never worked with a task runner, think of it as a batch file or, even better, a PowerShell script powered with some useful web development tools such as file concatenation, js/css shrinking, and more.

You will notice that, together with Gulp, we're also installing some Gulp-related plugins:

  • gulp-clean: This deletes the contents of the destination folders
  • gulp-concat: This merges multiple files into one
  • gulp-sourcemaps: This generates the Source Maps
  • gulp-typescript: This compiles TypeScript files into JS
  • gulp-uglify: This minifies JavaScript files

Configuring Gulp is as simple as adding the Gulp Configuration file to your project. You can do that just like you did with the NPM Configuration file, right-click on the root project node in the Solution Explorer and select Add, then New Item. From the client-side tree panel, locate and select the Gulp Configuration file and add a gulpfile.js file to the project root.

The Gulp configuration file, generally known as Gulpfile, is basically a list of tasks and commands that Gulp will execute, written using a mostly readable JavaScript syntax. Working with it can be tricky at first, especially if you want to use it to handle complex tasks. Digging too much into it would take us out of the scope of this book, so we'll just see how we can tell it to do a rather simple set of jobs, merge all the JavaScript files in the /Scripts/ folder, minus those in a directory for third-party scripts/libraries, into a single, minified all.min.js file which will be generated into the wwwroot folder. This can be obtained with the following code:

var gulp = require('gulp'),
    gp_clean = require('gulp-clean'), 
    gp_concat = require('gulp-concat'), 
    gp_sourcemaps = require('gulp-sourcemaps'), 
    gp_typescript = require('gulp-typescript'), 
    gp_uglify = require('gulp-uglify'); 
 
/// Define paths 
var srcPaths = { 
    app: ['Scripts/app/main.ts', 'Scripts/app/**/*.ts'], 
    js: ['Scripts/js/**/*.js'] 
}; 
 
var destPaths = { 
    app: 'wwwroot/app/', 
    js: 'wwwroot/js/' 
};
// Compile, minify and create sourcemaps all TypeScript files and place them to wwwroot/app, together with their js.map files. 
gulp.task('app', function () { 
    return gulp.src(srcPaths.app) 
        .pipe(gp_sourcemaps.init()) 
        .pipe(gp_typescript(require('./tsconfig.json').compilerOptions)) 
        .pipe(gp_uglify({ mangle: false })) 
        .pipe(gp_sourcemaps.write('/')) 
        .pipe(gulp.dest(destPaths.app)); 
}); 
 
// Delete wwwroot/app contents 
gulp.task('app_clean', function () { 
    return gulp.src(destPaths.app + "*", { read: false }) 
        .pipe(gp_clean({ force: true })); 
}); 
 
// Copy all JS files from external libraries to wwwroot/js 
gulp.task('js', function () { 
    return gulp.src(srcPaths.js) 
        // .pipe(gp_uglify({ mangle: false })) // disable uglify 
        // .pipe(gp_concat('all-js.min.js')) // disable concat 
        .pipe(gulp.dest(destPaths.js)); 
}); 
 
// Delete wwwroot/js contents 
gulp.task('js_clean', function () { 
    return gulp.src(destPaths.js + "*", { read: false }) 
    .pipe(gp_clean({ force: true })); 
}); 
 
// Watch specified files and define what to do upon file changes 
gulp.task('watch', function () { 
    gulp.watch([srcPaths.app, srcPaths.js], ['app', 'js']); 
}); 
// Global cleanup task 
gulp.task('cleanup', ['app_clean', 'js_clean']); 
 
// Define the default task so it will launch all other tasks 
gulp.task('default', ['app', 'js', 'watch']); 

Here's a brief explanation of what these commands actually do:

  • In lines 1-6, we set up Gulp and the required plugins. Notice that every one of them will be initialized into a variable, as that's the way Gulp works.
  • In lines 8-17,we define the file paths we will use. Doing that here will allow us to write them only once, thus making the Gulp file easier to maintain. Notice that we're splitting the source and the destination paths into two distinct objects, this will mentally help us to keep the development environment separated from the production one.
  • In lines 19-27, we have the app task, which is the most important and complex one. This task makes use of three different plugins: sourcemaps, typescript, and uglify. Notice how each line is bound to a specific plugin action, which takes care of a single job, following the "It should do one thing" paradigm Gulp is all about. Line 21 defines the source files, line 22 initializes the sourcemaps plugin, line 23 instructs the plugin to fetch the TypeScript compiler options from the tsconfig.json file, and so on.
  • In lines 29-33, we have the app_clean task, which will erase every file and subfolder within the wwwroot/app target path.
  • In lines 35-39, we define the js task, which is meant to publish external JavaScript libraries manually added to the project. This is a rather simple task, as it performs a mere copy of the .js files from a source folder into its production counterpart. Notice how we could easily choose to also minify and/or concatenate the source files into a single one by uncommenting a few lines of code. However, since this task is meant to handle third-party JavaScript libraries, it would be wise not to manipulate their code.
  • In lines 43-47, there's the js_clean task that will erase everything within the wwwroot/js target path.
  • In lines 49-52, we have the watch task. This is basically a file watcher/monitoring task that will execute the app and js tasks if one or more of their source files will change. 
  • In lines 54-55, we created a cleanup task that will erase the content of all destination paths by launching the app_clean and js_clean tasks. It can be useful to reset these folders to their initial state.
  • Last but not least, in lines 57-58, we define the default task that will execute all the other tasks, including watch, within a single call. This is the one we will launch.

Note

It's worth noting that the uglify plugin requires the mangle option to be set to false: this is a workaround for a bug in the Angular 2 RC5 that will hopefully be fixed in future releases. For more info regarding this topic, check out the following URL from the official Angular 2 GitHub repository:  https://github.com/angular/angular/issues/10618.

To execute the Gulp file, right-click on it from the Solution Explorer and choose Task Runner Explorer, or open it manually by selecting it from File, Other Windows in the Visual Studio 2015 main menu bar. Once opened, click the refresh button, then right-click the default task and select Run to activate it.

Working with Gulp

Once you do that, the watch task we just created will silently run in background, keeping tracks of our changes and act accordingly. Notice the default (running) word within the tab label, which will remind us that there is still an ongoing task: closing that console window will immediately shut it down.

That's everything we'll ever need from Gulp for this project; there's no need for us to go further. If you want to learn more about Gulp and its configuration file syntax, you will find a lot of resources and documentation, together with a lot of useful samples, in the official website at the following address: http://gulpjs.com/.

Dependent tasks

Since we're going to do a lot of modifications to the files contained in the /Scripts/app/ folder within the following chapters, it would be great if the app_clean task could run automatically before the app task: such behavior would ensure that we'll always get rid of outdated and orphaned files without having to manually perform the cleanup. Luckily enough, Gulp allows us to easily do that by passing an optional array of dependent tasks that will be launched before the main one.

Let's go back to the line where we defined the app task and add the following (updated code are highlighted):

gulp.task('app', ['app_clean'], function () { 

That's it. From now on the app task will launch the app_clean dependent task and wait for its completion before executing itself, meaning that the /wwwroot/app/ folder contents will be erased before the arrival of the new file, this is precisely what we wanted.

Note

It's worth noting that the synchronous behavior of the app_clean task is guaranteed by its return value, whenever a dependent task is returning itself, the main task will wait for its completion before running.

Using Grunt instead of Gulp

Before Gulp was widely welcomed by the web application development community, Grunt used to be the king of the hill. Does that mean the former killed the latter? Well, most certainly not. Grunt is still an excellent tool and can be a great alternative to Gulp, especially if you already know how to use it. Unless you do, though, we suggest starting with Gulp because we think that it has a fair edge on code flexibility, and also a more streamlined approach. However, if you don't feel like using it, sticking to Grunt and its renowned plugins is just as good, you won't ever get fired for such a choice.

Using Grunt instead of Gulp is as easy as doing the following:

  1. In NPM'spackage.json file, replace the Gulp references - together with the relevant plugins, with the Grunt packages.
  2. In the Solution Explorer, add a gruntfile.js file instead of a gulpfile.js.
  3. In the gruntfile.js, rewrite the same tasks defined previously using the Grunt syntax.

This is a good example of the Grunt-powered package.json:

{ 
  "version": "1.0.0", 
  "name": "opengamelistwebapp", 
  "private": true, 
  "dependencies": { 
  }, 
  "devDependencies": { 
    "grunt": "^0.4.5", 
    "grunt-contrib-clean": "^1.0.0", 
    "grunt-contrib-copy": "^1.0.0", 
    "grunt-contrib-uglify": "^1.0.0", 
    "grunt-contrib-watch": "^0.6.1", 
    "grunt-ts": "^5.3.2", 
  } 
} 

And this is how the gruntfile.js would look after porting all the Gulp tasks defined previously to Grunt syntax:

module.exports = function (grunt) { 
    grunt.loadNpmTasks('grunt-contrib-clean'); 
    grunt.loadNpmTasks('grunt-contrib-copy'); 
    grunt.loadNpmTasks('grunt-contrib-uglify'); 
    grunt.loadNpmTasks('grunt-contrib-watch'); 
    grunt.loadNpmTasks('grunt-ts'); 
 
    grunt.initConfig({ 
        clean: [ 
            'Scripts/app/*', 
            'Scripts/js/*' 
        ], 
        ts: { 
            base: { 
                src: [ 
                    'Scripts/app/main.ts', 
                    'Scripts/app/**/*.ts' 
                    ], 
                outDir: 'wwwroot/ app', 
                tsconfig: './tsconfig.json' 
            } 
        }, 
         
        uglify: { 
            my_target: { 
                files: [{ 
                    expand: true, 
                    cwd: 'wwwroot/ app', 
                    src: ['**/*.js'], 
                    dest: 'wwwroot/ app' 
                }] 
            }, 
            options: { 
                sourceMap: true 
            } 
        }, 
 
        // Copy all JS files from external libraries and required NPM packages to wwwroot/js 
        copy: { 
            main: { 
                files: [{ 
                    expand: true, 
                    flatten: true, 
                    src: [ 
                        'Scripts/js/**/*.js' 
                    ], 
                    dest: 'wwwroot/js/', 
                    filter: 'isFile' 
                }] 
            } 
        }, 
 
        // Watch specified files and define what to do upon file changes 
        watch: { 
            scripts: { 
                files: [ 
                    'Scripts/**/*.ts', 
                    'Scripts/**/*.js', 
                    ], 
                tasks: ['clean', 'ts
', 'uglify', 'copy'] 
            } 
        } 
    }); 
 
    // Global cleanup task 
    grunt.registerTask('cleanup', ['clean']); 
 
    // Define the default task so it will launch all other tasks 
    grunt.registerTask('default', ['clean', 'ts', 'uglify', 'copy', 'watch']); 
}; 

As we can see, the syntax is very different but the underlying logic and features are quite similar, we still have a bunch of dedicated tasks to handle TypeScript files, minify actions, copy operations, and monitor content change, and then the default one to wrap everything up. Notice how, just like we did with Gulp, the TypeScript compiler options are fetched from the tsconfig.json file so we don't have to write them a second time.

Note

To learn more about Grunt and its configuration file syntax you can visit the official website at the following address: http://gruntjs.com/.

That's basically all we need to know about task runners. Too bad we don't yet have any TS and/or JS files to properly test what we just did! Don't worry, though, we'll get there soon enough. Before that, let's finish our NPM journey by bringing the last (but not least) of our players into the loop.

Adding Angular 2

There are basically two ways to get Angular 2, both with their pros and cons, using NPM, which implies fetching the latest build and hosting the code within our project, or by a dedicated set of links to a suitable CDN.

Using NPM

To install Angular 2 using NPM we need to open the package.json file and add a bunch of packages under the dependencies node. This is how the file should look like at the end (Angular lines are highlighted):

{ 
  "version": "1.0.0", 
  "name": "OpenGameListWebApp", 
  "private": true, 
  "dependencies": { 
    "@angular/common": "2.0.0-rc.5",

    "@angular/compiler": "2.0.0-rc.5",

    "@angular/core": "2.0.0-rc.5",

    "@angular/http": "2.0.0-rc.5",

    "@angular/platform-browser": "2.0.0-rc.5",

    "@angular/platform-browser-dynamic": "2.0.0-rc.5",

    "@angular/upgrade": "2.0.0-rc.5",

    "core-js": "^2.4.1",

    "reflect-metadata": "^0.1.3",

    "rxjs": "5.0.0-beta.6",

    "systemjs": "^0.19.37",

    "zone.js": "^0.6.12" 
  }, 
  "devDependencies": { 
    "gulp": "^3.9.1", 
    "gulp-clean": "^0.3.2", 
    "gulp-concat": "^2.6.0", 
    "gulp-sourcemaps": "^1.6.0", 
    "gulp-typescript": "^2.13.6",
    "gulp-uglify": "^2.0.0", 
    "typescript": "^1.8.10" 
  } 
} 

The packages starting with the @ symbol are part of the Angular 2 bundle, which, at the time of writing, reached its Release Candidate 5 (RC5) development stage. The other ones are a set of ES6 shims (core-js), polyfills (reflect-metadata), loading libraries (systemjs), dependencies (rxjs), and helper tools (zone.js).

All of these packages are required for a number of good reasons:

  • They ensure backward-compatibility between the new ECMAScript v6 (ES6) language features - used by most Angular 2 packages, and ECMAScript v5 (ES5), which will be our compilation target.
  • They make our code compatible with the most common/used web browsers, including older ones.
  • They adopt a modern, module-based loading API to handle the required JS dependencies in an efficient way.
  • They use Angular 2 features to their full extent.

Note

All this information can be quite confusing, especially for old-school JavaScript developers, but don't worry, we're going to come back to these topics later on.

As usual, all of these packages will be downloaded in the background by the IDE as soon as you Save the file. Eventually, you will have a local instance of Angular 2 available under the following folder:

<project_root>/node_modules/@angular/ 

It's worth noting that we have also added a new script node that will execute a post-install command against the typings package we just added.

Now we need to move these files to the wwwroot folder. We can achieve this by adding a dedicated task to our Gulp configuration file as follows (new lines are highlighted):

/* 
This file in the main entry point for defining Gulp tasks and using Gulp plugins. 
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007 
*/ 
var gulp = require('gulp'), 
    gp_clean = require('gulp-clean), 
    gp_concat = require('gulp-concat'), 
    gp_sourcemaps = require('gulp-sourcemaps'), 
    gp_typescript = require('gulp-typescript'), 
    gp_uglify = require('gulp-uglify'); 
 
/// Define paths 
var srcPaths = { 
    app: ['Scripts/app/main.ts', 'Scripts/app/**/*.ts'], 
    js: [ 
        'Scripts/js/**/*.js', 
        'node_modules/core-js/client/shim.min.js',

        'node_modules/zone.js/dist/zone.js',

        'node_modules/reflect-metadata/Reflect.js',

        'node_modules/systemjs/dist/system.src.js',

        'node_modules/typescript/lib/typescript.js'

    ],

    js_angular: [

        'node_modules/@angular/**'

    ],

    js_rxjs: [

        'node_modules/rxjs/**'

    ] 
}; 
 
var destPaths = { 
    app: 'wwwroot/app/', 
    js: 'wwwroot/js/', 
    js_angular: 'wwwroot/js/@angular/',

    js_rxjs: 'wwwroot/js/rxjs/' 
}; 
 
// Compile, minify and create sourcemaps all TypeScript files and place them to wwwroot/app, together with their js.map files. 
gulp.task('app', ['app_clean'], function () { 
    return gulp.src(srcPaths.app) 
        .pipe(gp_sourcemaps.init()) 
        .pipe(gp_typescript(require('./tsconfig.json').compilerOptions)) 
        .pipe(gp_uglify({ mangle: false })) 
         .pipe(gp_sourcemaps.write('/')) 
        .pipe(gulp.dest(destPaths.app)); 
}); 
 
// Delete wwwroot/app contents 
gulp.task('app_clean', function () { 
    return gulp.src(destPaths.app + "*", { read: false }) 
        .pipe(gp_clean({ force: true })); 
}); 
 
// Copy all JS files from external libraries to wwwroot/js 
gulp.task('js', function () { 
    gulp.src(srcPaths.js_angular)

        .pipe(gulp.dest(destPaths.js_angular));

    gulp.src(srcPaths.js_rxjs)

        .pipe(gulp.dest(destPaths.js_rxjs)); 
    return gulp.src(srcPaths.js) 
        // .pipe(gp_uglify({ mangle: false })) // disable uglify 
        // .pipe(gp_concat('all-js.min.js')) // disable concat 
        .pipe(gulp.dest(destPaths.js)); 
}); 
 
// Delete wwwroot/js contents 
gulp.task('js_clean', function () { 
    return gulp.src(destPaths.js + "*", { read: false }) 
    .pipe(gp_clean({ force: true })); 
}); 
 
// Watch specified files and define what to do upon file changes 
gulp.task('watch', function () { 
    gulp.watch([srcPaths.app, srcPaths.js], ['app', 'js']); 
}); 
 
// Global cleanup task 
gulp.task('cleanup', ['app_clean', 'js_clean']); 
 
// Define the default task so it will launch all other tasks 
gulp.task('default', ['app', 'js', 'watch']); 

As you can see, it hasn't changed much, we just added a bunch of JS files that we need to copy from the /node_modules/ folder into the wwwroot/js/ folder, the same one we were already using to host third-party libraries. There's nothing odd there, NPM package files are external libraries, after all. For this very reason, it is also preferable to restrain ourselves from uglifying and/or concatenating them. It's also worth noting that we defined a separate, dedicated folder for Angular 2 and Rxjs packages, since both of them are spanned into multiple files.

Adding Typings

Before moving forward, there is another thing we need to take care of. Since we plan to transpile our TypeScript code into ECMAScript5, we have added the core-js NPM module into our package.json file. In case you've never heard of it, let's just say that it happens to be a standard JavaScript library providing a great set of polyfills for ES6, which is precisely what we need.

Note

If you would like to know more about the core-js, here's the URL to the project's official GitHub repository:  https://github.com/zloirock/core-js.

The only problem is that it doesn't come with a proper TypeScript definition file, meaning that both of our TypeScript compilers, either Visual Studio's or Gulp's, won't be aware of its existence, thus throwing a fair amount of TS2304 (type not found) exceptions upon each build attempt.

The best thing we can do in order to fix that is to add the proper type definitions to our project. To do that, open the package.json file again and add the following (new lines are highlighted):

{ 
  "version": "1.0.0", 
  "name": "opengamelistwebapp", 
  "private": true, 
  "dependencies": { 
    "@angular/common": "2.0.0-rc.5", 
    "@angular/compiler": "2.0.0-rc.5", 
    "@angular/core": "2.0.0-rc.5", 
    "@angular/http": "2.0.0-rc.5", 
    "@angular/platform-browser": "2.0.0-rc.5", 
    "@angular/platform-browser-dynamic": "2.0.0-rc.5", 
    "@angular/upgrade": "2.0.0-rc.5", 
 
    "core-js": "^2.4.1", 
    "reflect-metadata": "^0.1.8", 
    "rxjs": "5.0.0-beta.6", 
    "systemjs": "^0.19.37", 
    "typings": "^1.3.2", 
    "zone.js": "^0.6.12" 
  }, 
  "devDependencies": { 
    "gulp": "^3.9.1", 
    "gulp-concat": "^2.6.0", 
    "gulp-sourcemaps": "^1.6.0", 
    "gulp-typescript": "^2.13.6", 
    "gulp-uglify": "^2.0.0", 
    "typescript": "^1.8.10" 
  }, 
  "scripts": {

    "postinstall": "typings install dt~core-js --global"

  } 
} 

We can see that there are two new things here:

  • A new NPM package called typings, which is a TypeScript type definition manager. In other words, a tool we can use to retrieve type definitions from the web.
  • A whole new script key containing a small command that will be executed during the post-install phase. This basically means that the script will trigger every time we change something within the package.json file, right after all the NPM modules are retrieved and installed (or removed).

As soon as we Save our package.json file, a new typings folder will be added to our OpenGameListWebApp project's root, containing the type definition file we need.

Tip

If typings didn't install successfully during the Save, try to use the Restore Package option by right-clicking on the project's Dependencies node. Another way is to use the command line to install the typings explicitly. To do this, navigate to the root folder of your app and press CTRL+SHIFT, then select Open command window here. In the command line, type the following command: > npm run typings install That should do the trick.

Using a CDN

At the time of writing, the only valid CDN hosting Angular 2 updated builds is npmcdn.com. If we want to use it, we can avoid updating our gulpfile.js file and wait until we are working on the index.html and systemjs.config.js files. Keep reading, as we'll get there shortly.

Upgrading the typescriptServices.js file

At the time of writing, adding Angular 2 via NPM would produce the following TypeScript compilation error:

Invalid module name in augmentation, module '../../Observable' cannot be found.

This is due to a known bug in the TypeScript version currently shipped with Visual Studio 2015. The most effective way to fix that is to replace this local VS2015 file:  C:Program Files (x86)Microsoft Visual Studio 14.0Common7IDECommonExtensions MicrosoftTypeScript ypescriptServices.js.

With the following remote file:

https://raw.githubusercontent.com/Microsoft/TypeScript/Fix8518/lib/typescriptServices.js.

Doing that will also fix a couple more errors we will most likely get in Chapter 3Angular 2 Components and Client-Side Routing. Needless to say, it's highly advisable to make a backup of the original file before replacing it.

Additional information regarding the bug is available through the following URLs:  https://github.com/Microsoft/TypeScript/issues/8518 and  https://github.com/Microsoft/TypeScript/pull/7507.

Setting up the client code

It's time to lay down the first bricks of our app, a working skeleton, used as a starting point for what we're going to do in the next chapters. It will consist of:

  • An Angular 2 component file: Scripts/app/app.component.ts
  • An Angular 2 module file: Scripts/app/app.module.ts
  • An Angular 2 bootstrap file: Scripts/app/main.ts
  • A module loader configuration file: wwwroot/systemjs.config.js
  • An HTML file to wrap everything up: wwwroot/index.html

The component file

The component is the most basic and fundamental concept in Angular 2. Think of it like a class that controls a specific piece of a web page where we can either display some data to each user and/or respond to their feedbacks. We can say that our Angular 2 app will be almost entirely built upon multiple Components serving specific purposes-most of them will be reusable, others will be only used once. They can also either be as small as a few lines or they can result in a ridiculously long piece of code.

Luckily, our first component is quite simple. In the Solution Explorer, right-click on the /Scripts/app/ folder and add a new app.component.ts file:

The component file

Once created, feed it with the following code:

import {Component} from "@angular/core"; 
 
@Component({ 
    selector: "opengamelist", 
    template: `<h1>OpenGameList</h1><div>Work in progress...</div>` 
}) 
 
export class AppComponent { } 

That's it. Let's see what we just did here in detail:

  • In line 1, we're importing the Component function from the Angular 2 main library, which is @angular/core. The Component function is what we need to define a Component's metadata for a class, which basically means being able to make Components, this is a required step for what we'll do next.
  • In line 3-6, we're creating our first Component by applying the Component function to the class. In TypeScript, we do that by prefixing it with the @ symbol and invoking it just above the component class. In other words, the @Component instruction tells Angular that this class is an Angular component. Notice that the selector and template fields are passed as a configuration object, as we will analyze them soon enough.
  • In line 8, we're defining the AppComponent class. Notice the export keyword, which will allow us to import it from other components. The act of exporting makes our app.component.js file a module.

A word on components and modules

Angular 2 is a modular framework: this means that Angular 2 apps are also modular, as they consist of many files dedicated to a specific purpose. Typically, most application files export a single Componentclass, which is a class bundled with component metadata. Our app.component.js file, for example, exports the AppComponent class decorated with its component metadata, thus being a Module file exporting a Component class. We could also say that the Component is the content, while the Module is the file itself. We'll see how to import Components in the next chapter.

The module file

Angular Modules, also known as NgModules, have been introduced in Angular 2 RC5, which is the latest version at the time of writing, and are a great and powerful way to organize and bootstrap any Angular 2 application. They help developers to consolidate their own set of components, directives, and pipes into reusable blocks.

Note

If you've already played with previous versions of Angular 2, you will find the following URL useful to understand the transition by checking out the following URL: 

https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html.

Every Angular 2 application since RC5 must have at least one module, which is conventionally called the root module and given the AppModule class name.

From the Solution Explorer, right-click on the /Scripts/app/ folder, add a new app.module.ts file, and fill it with the following code:

///<reference path="../../typings/index.d.ts"/> 
import {NgModule} from "@angular/core"; 
import {BrowserModule} from "@angular/platform-browser"; 
import {HttpModule} from "@angular/http"; 
import "rxjs/Rx"; 
 
import {AppComponent} from "./app.component"; 
 
@NgModule({ 
    // directives, components, and pipes 
    declarations: [ 
        AppComponent 
    ], 
    // modules 
    imports: [ 
        BrowserModule, 
        HttpModule 
    ], 
    // providers 
    providers: [ 
    ], 
    bootstrap: [ 
        AppComponent 
    ] 
}) 
export class AppModule { } 

Again, let's take a look at what we just wrote:

  • In line 1, we added a reference to the type definitions we fetched during the previous section to ensure our TypeScript compiler(s) could find it. Notice that, if we're using a CDN or a pre-compiled version of Angular 2, we could (and should) definitely remove this line.
  • In lines 2-4, we import the basic Angular 2 modules that we will need right from the start.
  • In line 5, we import the rxjs library definition file(s), which will be useful to compile the Angular 2 libraries.
  • In line 7, right after the first empty line, we import the application root component that we just wrote.
  • In line 9, we declare our root NgModule. As we can see it consists in an array of named arrays, each one containing a set of Angular 2 objects that serves a common purpose: directives, components, pipes, modules, and providers. The last one of them contains the component(s) we want to bootstrap, which in our case is the AppComponent one.

Working with the root module only is a very viable approach until the Angular 2 app grows to a certain size. When it becomes bigger, it will be more practical to refactor it into a number of feature modules, each one of them grouping together a set of related tasks.

The bootstrap file

Now that we have our root module, we need to bootstrap it. From the Solution Explorer, right-click on the /Scripts/app/ folder and add a new main.ts file, then fill it with the following code:

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

Now we're just missing an entry point to load with the browser. Let's add it right now.

The module loader configuration file

In this application, we're going to use the SystemJS module loader library to load our application and all the required Angular 2 modules and dependencies. In order to do that, we have to add a systemjs.config.js file to the wwwroot folder and define a number of configuration rules within it, as follows:

(function (global) { 
    // map tells the System loader where to look for things 
    var map = { 
        'app': 'app', // our application files 
        '@angular': 'js/@angular', // angular2 packages 
        'rxjs': 'js/rxjs' // Rxjs package 
    }; 
 
    // packages tells the System loader which filename and/or extensions to look for by default (when none are specified) 
    var packages = { 
        'app': { main: 'main.js', defaultExtension: 'js' }, 
        'rxjs': { defaultExtension: 'js' } 
    }; 
 
    // configure @angular packages 
    var ngPackageNames = [ 
      'common', 
      'compiler', 
      'core', 
      'http', 
      'platform-browser', 
      'platform-browser-dynamic', 
      'upgrade', 
    ]; 
 
    function packIndex(pkgName) { 
        packages['@angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' }; 
    } 
 
    function packUmd(pkgName) { 
        packages['@angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' }; 
    }; 
 
    var setPackageConfig = System.packageWithIndex ? packIndex : packUmd; 
    ngPackageNames.forEach(setPackageConfig); 
    var config = { 
        map: map, 
        packages: packages 
    } 
    System.config(config); 
})(this); 

The code is pretty much self-documented with inline comments, yet it could be useful to highlight the most relevant tasks:

  • The map variable will host the three dynamic packages we're using SystemJS for, all of them relative to /wwwroot/: app for our application; js/@angular for Angular 2 and js/rxjs for Rxjs.
  • The packages variable will set the default filename and/or extension values for each package. These will be used whenever we define an import statement without specifying them.
  • The rest of the file is dedicated to dynamically loading the Angular 2 built-in packages.

If we want to use a CDN instead of relying upon the local JS folder, we only need to perform a minor update within the previous code, in the map section, as follows:

    var map = { 
        'app': 'app', // our application files 
        '@angular': 'js/@angular', // angular2 packages 
        'rxjs': 'https://npmcdn.com/[email protected]' // Rxjs package (CDN) 
    }; 

And also within the two module loader functions:

    function packIndex(pkgName) { 
        packages['https://npmcdn.com/' + pkgName] = { main: 'index.js', defaultExtension: 'js' }; 
    } 
    function packUmd(pkgName) { 
        packages['https://npmcdn.com/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' }; 
    }; 

Note

For further info regarding SystemJS and its Configuration API, including advanced options, we strongly suggest reading the official documentation on the project's GitHub page: 

https://github.com/systemjs/systemjs and https://github.com/systemjs/systemjs/blob/master/docs/config-api.md.

Why use a dynamic module loader?

Before going further, it might be useful to explain why we worked so hard with a module loader instead of adding all the relevant JS files into the index.html file right from the start.

To keep it simple, we did it because it's the only way to efficiently handle any modern JavaScript modular system such as Angular 2, Rxjs and also all applications based upon them, including the one we're working on right now.

What's a modular system exactly? It's nothing more than a package, library, or application split into a series of smaller files which depend on each other using reference statements such as import, require, and so on. ASP.NET, Java, Python, and most compilation-based languages have it. That's not the case with script-based languages such as PHP and JavaScript: they that are doomed to pre-load everything in the memory before being able to determine whether they'll be using it or not. All of these changes, with the introduction of ECMAScript 6 (ES6), bring a fully-featured module and dependency management solution for JavaScript. SystemJS basically acts as an ES6-polyfill for browsers that don't support it already, allowing us to get that module system working in modern browsers. Since both Angular 2 and Rxjs leverage that dynamic-loading approach, implementing it within our project will result in a huge performance gain.

Note

Keep in mind that SystemJS is not the only choice we have to load Angular 2 packages: there are other good choices out there, for example the popular module bundler known as webpack. Should we want to use that instead, here's a great guide for doing that: 

https://angular.io/docs/ts/latest/guide/webpack.html.

The index.html file

The HTML file serves two main purposes: being an entry point for the browser so it can load the client-script files and execute the application, and laying out the DOM structure used by Angular 2 to display it. In the Solution Explorer, right-click on the wwwroot folder and add a new index.html file, then fill it with the following code:

<!DOCTYPE html> 
<html> 
<head> 
    <base href="/"> 
    <title>OpenGameList</title> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
 
    <!-- Step 1. Load libraries --> 
    <!-- Polyfill(s) for older browsers --> 
    <script src="js/shim.min.js"></script> 
    <script src="js/zone.js"></script> 
    <script src="js/Reflect.js"></script> 
    <script src="js/system.src.js"></script> 
 
    <!-- Step 2. Configure SystemJS --> 
    <script src="systemjs.config.js"></script> 
    <script> 
      System.import('app').catch(function(err){ console.error(err); }); 
    </script> 
</head> 
 
<!-- Step 3. Display the application --> 
<body>     
    <!-- Application PlaceHolder --> 
    <opengamelist>Loading...</opengamelist> 
</body> 
</html> 

After adding the index.html file, we need to set it as the main entry point when executing our app in the Debug mode. In the Solution Explorer, right-click on the project node and select Properties, then switch to the Debug tab and change the Launch URL parameter accordingly.

The index.html file

Note

While we're here, we might also take the chance to set the HTTP port that will be used by the local web server during development by changing the numeric part of the App URL textbox value. As we can see in the preceding screenshot, we're going to use 14600 throughout the whole book.

If we want to use a CDN instead of local JS files, we can replace the <script> elements right below the Step 1 comment with the following:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/shim.min.js"></script> 
    <script src="https://npmcdn.com/[email protected]"></script> 
    <script src="https://npmcdn.com/[email protected]"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/ libs/systemjs/0.19.37/system.js"></script>

These are the latest versions at the time of writing. Be sure to replace them with the most recent Angular 2-compatible versions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset