A message to our Reflections community about COVID-19. Learn more ›

Our Blog

Using Angular 4 for Single Page Applications (SPAs) with ASP.NET Core 1.1

These days, many developers are building Single-Page Applications (SPAs) using frameworks such as Angular or React. These are powerful frameworks that produce a great end-user experience, but we often hear that building these applications is complicated. It can be a challenge to integrate server-side and client-side code well, or even just to choose a productive project setup in the first place, actively it's a little bit confusing and most tutorials I found is not fitting well due to different .NET Core version, different Angular version, or even different visual studio version template.

We will create a SPA Application, using :

  • Visual Studio 2017.
  • ASP.NET Core 1.1 (v1.1.2).
  • Angular 4 (v2.4.10).

let's start

1.Create an ASP.Net Core App

In Visual Studio 2017 click File, New, Project.

then select Templates, Visual C#, .Net Core Then ASP.NET Core Web Application.

Provide the name (I used SPA), the path or Location, I like to leave "Create directory for solution" checked, then press OK.

Make sure you you select ASP.NET Core 1.1

Select Web Application, leave Authentication as "No Authentication", then click OK

You should now see the solution file.

Click Ctrl-F5 on your keyboard, and you should build the solution, launch IIS Express and your default browser, and if all is well, see something like this, below, in your browser:


2.Adding Angular 2 QuickStart into our ASP.Net Core app

We'll be using the Angular 2 QuickStart app from the source on GitHub from here

If you need some further background, work through the excellent tutorials from the Angular 2 team here.

To get the Angular 2 QuickStart code merged into our ASP.Net Core MVC app, we'll start by getting a copy of the source.

There are a few ways to do this; you could download a ZIP of the latest source from GitHub, or you could clone a complete copy of the repository using Git Extensions or your favourite Git utility.

The command is the same, irrespective of where you do it, in my case I typed this into Powershell:

git clone https://github.com/angular/quickstart

this is the result:

Next navigate to the folder containing the files cloned above.

Copy the app folder (both folder and files in the folder) as well as these files: index.html, systemjs.config.extras.js, system.config.js, systemjs-angular-loader.js and main.ts to the Visual Studio 2017 solution's wwwroot folder.

In addition, copy the file tsconfig.json to the Visual Studio 2017 project's root directory.

If you copy files from QuickStart using windows explorer, you can paste the files directly into the project using Visual Studio 2017 solution explorer. You should see this when completed:

NOTE: The warning on missing dependencies is simply due to the npm dependencies that we will handle in the next step using the new package.json file. Additionally in Visual Studio 2015 the file systemjs.config.extras.js will be collapsed under the system.config.js - even if you copy them separately. It should be there in the wwwroot folder, and you can easily verify this by checking it.

Now we need to add package.json

add new Item to the project

select ASP.NET Core, Web, General Then npm Configuration File.

Edit the pachage.json file with the below

  "dependencies": {
    "@angular/common": "~2.4.0",
    "@angular/compiler": "~2.4.0",
    "@angular/core": "~2.4.0",
    "@angular/forms": "~2.4.0",
    "@angular/http": "~2.4.0",
    "@angular/platform-browser": "~2.4.0",
    "@angular/platform-browser-dynamic": "~2.4.0",
    "@angular/router": "~3.4.0",
    "angular-in-memory-web-api": "~0.2.4",
    "systemjs": "0.19.40",
    "core-js": "^2.4.1",
    "rxjs": "5.0.1",
    "zone.js": "^0.7.4"
  "devDependencies": {
    "typescript": "~2.0.10",
    "tslint": "^3.15.1"
  "repository": {}

As you hit save, after editing the file, you will likely see Visual Studio 2017 restoring the packages

Next we'll edit tsconfig.json, to the below:

  "compilerOptions": {
    "diagnostics": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [ "es2015", "dom" ],
    "listFiles": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "noImplicitAny": true,
    "outDir": "wwwroot",
    "removeComments": false,
    "rootDir": "wwwroot",
    "sourceMap": true,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es5"
  "exclude": [

Last of all, before we build, we have one typescript file to remove.

Go to the app folder under wwwroot and delete app.component.spec.ts

Since we have not included the tests in this project, nor the dependencies so it will create errors if left in place.

When the build is completed you should find the typescript files automatically "transpiled" in-place, and when unfolded reveal a javascript .js file as well as a map .js.map file for each

The .js javascript files will be executed in the browser, the .js.map files are used if and when you are debugging to link an issue in javascript back to the typescript source.

To test the build hit ctrl-F5 to compile and launch the browser, you should still see a working MVC page, At the default URL.

As well as at /home/index

To see the quickstart page, edit the URL to look at /index.html

You should see the angular loader:

But you will not see anything else, yet. Hit F12 and you should see Angular's error message in the console.

Which when unfolder is likely not all too helpful either:

The reason is that none of the javascript libraries required are served from their current location 

Look inside index.html and you see:

<!DOCTYPE html>
    <title>Angular QuickStart</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">
    <!-- 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/systemjs/dist/system.src.js"></script>
    <script src="systemjs.config.js"></script>
      System.import('app').catch(function(err){ console.error(err); });
    <my-app>Loading AppComponent content here ...</my-app>

The library files would be expected to be served from wwwroot/node_modules but they are actually under this, in the project root. This is not obvious unless you turn on the show all files option,

Then you will see the folder appear, greyed out to indicate it is not source controlled or part of the solution, but added separately as required.

You can re-hide the folders, as they can get distracting, and now we'll fix this issue.

We'll add a package Microsoft.AspNetCore.SpaServices NuGet

Right click the project root, click Manage NuGet Packages, select browse.

search for Microsoft.AspNetCore.SpaServices, Click on the package then click install.

Next open the file startup.cs in the editor, where we will edit the end of the file, where it has:

            app.UseMvc(routes =>
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

Change this to:

            app.UseStaticFiles(new StaticFileOptions
                FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "node_modules")),
                RequestPath = "/node_modules"
            app.UseMvc(routes =>
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

To satisfy dependencies for PhysicalFileProvider and Path.Combine we need to add this to the using's at the top of startup.cs

using Microsoft.Extensions.FileProviders;
using System.IO;

Save startup.cs and rebuild, hit ctr-F5 and once again browsing to index.html,or the root directory (as the changes above now mean the index.html file will "win" over the /home/index path when browsing the root directory.

Now the app should load correctly:

Importantly you can still browse the older ASP.Net Core MVC home page at /home/index by using the path explicitly, as below, since we'll be using these controller/action paths more as the project continues.