[dotnet new] Angular Single Page Application – Setup and How The Template Works

Jamie Taylor15 comments
Angular Single Page Application Header ImageSource: https://stocksnap.io/photo/LWSNFFFOJS Copyright 2017: Francis Daniel

Today’s header image was created by Francis Daniel, the original source for the image is available here

Prerequisites

The SPA templates (one of which we’ll be using in this post) require .NET Core SDK version 1.0.1 or above and node.js to be installed and set up on you development machine.

I’d recommend that you have both of these installed before you continue reading this article. Especially if you’re going to be coding along with it.

The latest version of node.js can be downloaded from the official website and the latest version of the .NET Core SDK is available from the official website for it, too.

Project Templates

In a recent post, I introduced the topic of .NET Core 1.0.1’s templating engine.

Go take a look at it here

Today, we’re going to take a look at the Angular2 single application template provided by Microsoft.

Angular2

Angular2 is an open source javascript framework for front end development, primarily designed to focus on the building of Single Page Applications. It does this by supporting both MVC (Model-View-Controller) and MVVM (Model-View-View Model) architectures.

Angular2 is the second iteration of the main Angular project, with Angular4 being the next version.

The reason for skipping version 3 is outlined here, on the official Angular blog

dotnet new

The first thing we need to do is figure out whether we have the Angular2 SPA template installed. To do this, bring up a terminal

Creating projects from these templates is not yet supported in any of the Visual Studio IDEs.

and enter the following command

dotnet new -all

as a side note: I prefer single hyphen switches, but you can replace ‘-all’ with ‘–show-all’ and get the same output

If you’re running .NET Core version 1.0.1 or above you should receive a response similar to this:

Template Instantiation Commands for .NET Core CLI.
Usage: dotnet new [arguments] [options]
Arguments:
template The template to instantiate.
Options:
-l|--list List templates containing the specified name.
-lang|--language Specifies the language of the template to create
-n|--name The name for the output being created. If no name is specified, the name of the current directory is used.
-o|--output Location to place the generated output.
-h|--help Displays help for this command.
-all|--show-all Shows all templates
Templates Short Name Language Tags
-----------------------------------------------------------------------------------------------------------
Console Application console [C#], F# Common/Console
Class library classlib [C#], F# Common/Library
Unit Test Project mstest [C#], F# Test/MSTest
xUnit Test Project xunit [C#], F# Test/xUnit
ASP.NET Core Empty web [C#] Web/Empty
ASP.NET Core Web App mvc [C#], F# Web/MVC
ASP.NET Core Web API webapi [C#] Web/WebAPI
Nuget Config nugetconfig Config
Web Config webconfig Config
Solution File sln Solution
Examples:
dotnet new mvc --auth None --framework netcoreapp1.1
dotnet new aurelia
dotnet new --help

This shows us that we have the templating engine installed, but we want to do a quick check as to whether the Angular template is installed. You can do this on a Unix/Linux OS by running the following command:

dotnet new -all | grep 'angular'

On Windows, you’ll need to run the following command:

dotnet new -all | findstr /I "angular"

Either way, if you have the Angular template installed the response should look like this:

MVC ASP.NET Core with Angular angular [C#] Web/MVC/SPA

The format of the response is correct at the time of writing

If you get a blank response (as I would have done, based on the output of ‘dotnet new -all’ from earlier), then you don’t have the Single Page Applications templates installed. To install them, run the following command:

dotnet new --install Microsoft.AspNetCore.SpaTemplates::*

And they will be installed.

Also take a look at my post on the templating engine, as there’s some useful information in there

New Angular SPA

Now that we have confirmed that we’ve got the templates installed, we need a directory to store the Angular SPA code in:

mkdir angular-spa
cd angular-spa

Now we need to create a new project from the template:

dotnew new angular

Before we can begin development though we need to restore the .NET Core packages:

dotnet restore

Then restore the node packages

As we’ll see in a moment, this template uses some node things

npm install -d

Restoring the Node packages will take longer than the .NET Core packages, as there are quite a lot of Node packages to pull down.

If you take a look at the directory structure again, you’ll notice a new directory is listed: node_modules.

node_modules

A quick side note for those who haven’t done any node.js and npm stuff before:

This is going to be an over simplified explanation, so some things aren’t going to be 100% correct

node.js is a server-side JavaScript environment, and is usually used to develop JavaScript applications and to run client side unit tests.

It can do a lot more than just that, but we’ll stick with this explanation for now.

When we ran the command:

npm install -d

we told node to use npm (node package manager) to take a look through the package.json file and install all of the node packages it found there. The ‘-d’ switch told npm to install them locally (in the directory where our packages.json file is located), rather than in the system wide cache of packages.

‘-g-‘ is the switch for installing the packages globally

Installing node packages locall is the default action for npm.

There are many reasons for this, the main one being that we might have two different projects which rely on different versions of the same node packages. If so, which do we store in the system wide package cache? It makes more sense to store the specific packages relating to a given project in the development directory for that project.

To learn more about node.js, I would recommend taking a look through Learn You Node.

Thanks swyx on the {CodingBlocks}.NET slack channel for the suggestion

Running the SPA

Now that we have the project set up and have restored our packages, it’s time to run this bad boy

We’ll open it in an IDE in a moment, but let’s continue with the terminal for now

dotnet run

And if you head to the URL that .NET Core gives you (mine was localhost:5000), you should see something very similar to this.

Angular SPA First Boot
Everything here is being added to the DOM via Angular

How It All Fits Together

From the rest of this article, I’ll be using Visual Studio for Mac.

I also closed the open application in the terminal

Before we begin however, I just wanted to show a quick comparison of how VS for Mac and VS Code display the directory structure for our project:

VS for Mac and VS Code Directory Comparison
VS for Mac on the left, VS Code on the right

Aside from the size of the text and the file type icons used, VS Code shows all of the sub-directories within our project whereas VS for Mac hides the bin, obj and node-modules directories. Just something that might be worth noting, going forward.

Personally, I prefer the way that VS Code displays things.

Dependencies

Taking a look at our directory structure, at the top are our NuGet and SDK dependencies.

Angular SPA Directory Structure - Dependencies

The first two NuGet dependencies (AspNetCore and AspNetCore.Mvc) and the single SDK one (.NETCore.App) are for .NET Core and .NET Core’s MVC frameworks.

AspNetCore.StaticFiles allows us to serve static files from our wwwroot directory.

We’ll be using this to serve our bundled JavaScript

Extensions.Logging.Debug is used for logging purposes.

The newest package here is the piggy in the middle: AspNetCore.SpaServices. This package supplies us with a lot of things, but the relevant things it supplies are:

  • server-side prerendering of Angular and React code
  • webpack middlewear
  • hot module replacement
Server-Side Prerendering

Server-side prerendering is an amazing feature. When I wrote about bundling a while back, I’d talked a little about how browsers work.

This is the post I’m referring to. Go take a look for a longer description than I’m about to give here

I’d mentioned that everything required to render a page (the markup, CSS, JS, images, etc.) has to be downloaded before a page can be rendered by the browser. The browser will have to wait until those downloads are finished before rendering the page (unless you do some smart things with lazy loading) and showing it to the user.

Bundled JavaScript code tends to be quite big for single page applications which is why we usually use minification on our JS files. However, those minified JS files still need to be downloaded to the user’s browser, THEN processed before the user can see and interact with the page.

This is where server-side rendering comes in. The basic version is that .NET Core will run the Angular or React+Redux code on the server, after creating the HTML response.

This has the double whammy of producing HTML with all of the initial Angular or React+Redux components built into and part of the rendered HTML. Meaning that the user doesn’t have to wait for the bundled JS to download and for Angualr or React to inject the relevant components for the selected view.

It also means that the single page application can, technically, boot and run (some features) without JavaScript running on the client’s machine.

Web crawlers tend to run without JavaScript being present or enabled

webpack Middleware

webpack is currently the hottest thing in the JavaScript-based bundling tools canon.

It’s also the only JS tool that I know of, which has it’s own (official) beach towels

Each of the SPA templates uses webpack to package all of the dependencies for your application.

I’ve written about webpack before

The way that webpack works is that you give it a JavaScript module as a starting point and it builds a graph for all of the CommonJs modules that the starting module depends on. It then bundles them all together into one large CommonJS module.

Jeffrey T. Fritz explains what the webpack middleware does, rather succinctly, in this paragraph (taken from the SPA Templates announcement blog post):

Webpack dev middleware is integrated into these projects via SpaServices. As long as your application is running in the Development environment, you can modify your client-side code (e.g., the TypeScript, or in Angular, the html files that are compiled into your components), and the updated version is almost immediately available to the browser. You don’t have to run any build commands manually.

The above quote is taken from: https://blogs.msdn.microsoft.com/webdev/2017/02/14/building-single-page-applications-on-asp-net-core-with-javascriptservices/

So each time that you write a change to any of the client side code to disk, webpack steps in and rebundles everything for you.

whenever you’re in dev, anyway

You can see this being used in the Configuration method in the startup.cs file:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}

Hot Module Replacement

This is the most impressive step of the bunch and builds on the webpack middleware. With Hot Module Replacement enabled, the webpack middleware is given a direct line of communication to the local browser you are using to test your development code on.

As you make changes, and the webpack middleware bundles those changes for you, the new versions of those bundles are pushed to the local browser and the new code is injected directly into your session WITHOUT causing a page refresh – meaning that your state is kept between changes and rebundle actions.

Views

It’s worth taking a really quick look in the views directory to get a sense of what has changed recently, and some of the new tag helpers.

Angular SPA Directory Structure - Views

Taking a look at the ViewImports file, we can see the new tag helpers being imported

@using angular_spa
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNetCore.SpaServices"

And we can see those tag helpers being used in the index view (within the Home directory)

@{
ViewData["Title"] = "Home Page";
}
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>
}

The JavaScript files that are referenced on lines 7 and 9

@{
ViewData["Title"] = "Home Page";
}
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>
}

are being served as static files from the wwwroot directory.

Just how I said we would use them earlier.

The tag helper on line 5 comes from the SpaServices namespace

@{
ViewData["Title"] = "Home Page";
}
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>
}

and is what is used to render the JS on the server before injecting it into the response.

ClientApp

The ClientApp directory is where all of the client side Angular code that we will create and edit is located.

Angular SPA Directory Structure - ClientApp

We’ll skip over the dist and test directories and take a look at how the Angular modules are brought together.

The first thing that you’ll notice is that everything is divided into components (which is a modern SPA practise adopted by Angular, React, and other JavaScript frameworks. Each of these components are built up of three files:

  • <componentName>.component.css
  • <componentName>.component.html
  • <componentName>.component.ts

The SPA templates that Microsoft have released all use TypeScript rather than JavaScript, but you can still use JavaScript if you wish.

TypeScript is transpiled to JavaScript anyway

Each of these groups of files are Angular specific components. Let’s take a look at one, before we take a look at the app.module.ts file.

Home Component

We’ll take a look at the home component as it’s the simplest of the components.

The home component is up of made of two files:

  • home.component.html
  • home.component.ts

Taking a look at the home.component.ts file

import { Component } from '@angular/core';
@Component({
selector: 'home',
templateUrl: './home.component.html'
})
export class HomeComponent {
}

The first thing that we’re doing is importing the core Angular code. This will expose all of the core Angular functions and interfaces. The import keyword (when used with webpack) works in a similar way to how the require keyword works with requireJs.

After that, we’re defining an Angular component and supplying it some metadata about the component

import { Component } from '@angular/core';
@Component({
selector: 'home',
templateUrl: './home.component.html'
})
export class HomeComponent {
}

This includes setting up an Angular routing selector, and the template HTML. When the home route is selected, Angular will find the component which matches (we’ll see how the routing works in a moment), will render the template HTML and inject the TypeScript classes that are included in the component (in this case the HomeComponent class).

Then we define an empty TypeScript class called HomeComponent (which contains our component).

import { Component } from '@angular/core';
@Component({
selector: 'home',
templateUrl: './home.component.html'
})
export class HomeComponent {
}

The home.component.html file is a simple html document, and I’ll include here for completeness sake:

<h1>Hello, world!</h1>
<p>Welcome to your new single-page application, built with:</p>
<ul>
<li><a href='https://get.asp.net/'>ASP.NET Core</a> and <a href='https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx'>C#</a> for cross-platform server-side code</li>
<li><a href='https://angular.io/'>Angular 2</a> and <a href='http://www.typescriptlang.org/'>TypeScript</a> for client-side code</li>
<li><a href='https://webpack.github.io/'>Webpack</a> for building and bundling client-side resources</li>
<li><a href='http://getbootstrap.com/'>Bootstrap</a> for layout and styling</li>
</ul>
<p>To help you get started, we've also set up:</p>
<ul>
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li>
<li><strong>Server-side prerendering</strong>. For faster initial loading and improved SEO, your Angular 2 app is prerendered on the server. The resulting HTML is then transferred to the browser where a client-side copy of the app takes over.</li>
<li><strong>Webpack dev middleware</strong>. In development mode, there's no need to run the <code>webpack</code> build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.</li>
<li><strong>Hot module replacement</strong>. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, your Angular 2 app will be rebuilt and a new instance injected is into the page.</li>
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and the <code>webpack</code> build tool produces minified static CSS and JavaScript files.</li>
</ul>
app-module

Each of the components is added to the app-module.ts file via import commands (which are picked up by webpack).

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
NavMenuComponent,
CounterComponent,
FetchDataComponent,
HomeComponent
],
imports: [
UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])
]
})
export class AppModule {
}

The first eight lines are our module import commands, after that we define the NgModule for our AppModule class:

@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
NavMenuComponent,
CounterComponent,
FetchDataComponent,
HomeComponent
],
imports: [
UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])
]
})

@NgModule takes a metadata object that tells Angular how to compile and launch the application.

  • imports — the BrowserModule that this and every application needs to run in a browser.
  • declarations — the application’s lone component, which is also …
  • bootstrap — the root component that Angular creates and inserts into the index.html host web page.

The above quote is taken from the Angular documentation on the Root Module

We’re telling the app-module to bootstrap with the AppComponent

again, we’ll come back to that in a moment

We’re then declaring all of our components and our imports (which takes our components and registers the routing for them.

Our routing is set up in the following lines:

RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])

This is a little similar to .NET Core’s MVC routing, except that it is more explicit as to which components are used for the routes supplied.

RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])

For example, the highlighted line in the above code block ensures that the route ‘home’ loads the HomeComponent.

Unlike MVC’s routing, single components can be used in multiple routes.

MVC’s controllers can respond to multiple different routes, using the Route attribute. But this is not an accepted practise and can lead to confusing code.

App Component

Which brings us to the App Component. This is the component that we’re bootstrapping with Angular.

Bootstrapping in this context means that Angular will take the components listed (the bootstrap object takes an array of components) and insert it into the DOM for the index.html at boot.

While you can put more than one component tree on a host web page, that’s not typical. Most applications have only one component tree and they bootstrap a single root component.

You can call the one root component anything you want but most developers call it AppComponent.

Taken from the Angular documentation on Boostrapping

Taking a look at the HTML template for app.component

<div class='container-fluid'>
<div class='row'>
<div class='col-sm-3'>
<nav-menu></nav-menu>
</div>
<div class='col-sm-9 body-content'>
<router-outlet></router-outlet>
</div>
</div>
</div>

We can see two DOM elements which don’t seem to have correct HTML names:

  • nav-bar
  • router-outlet

I’ve highlighted them for your convinence

router-outlet is similar to the @RenderBody helper in C# – it is swapped out by Angular, replacing it with the rendered HTML template content of the selected component.

nav-bar isn’t an Angular specific DOM element, but is used by the navmenu component. We can see this, if we take a look at the navmenu.component.ts file

import { Component } from '@angular/core';
@Component({
selector: 'nav-menu',
templateUrl: './navmenu.component.html',
styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent {
}

Looking at the highlighted line shows us that the navmenu component uses a selector.

The bootstrapping process sets up the execution environment, digs the root AppComponentout of the module’s bootstrap array, creates an instance of the component and inserts it within the element tag identified by the component’s selector.

Taken from the Angular documentation on Bootstrapping

This means that Bootstrapper inserts the contents of the navmenu component into the DOM wherever it finds the nav-menu selector. Which is extremely handy if you want to add static looking (but dynamically created) content into the DOM.

Navmenu Component

And the very last thing that we’ll look at is the HTML of the navmenu component

<div class='main-nav'>
<div class='navbar navbar-inverse'>
<div class='navbar-header'>
<button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
<span class='sr-only'>Toggle navigation</span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
</button>
<a class='navbar-brand' [routerLink]="['/home']">angular_spa</a>
</div>
<div class='clearfix'></div>
<div class='navbar-collapse collapse'>
<ul class='nav navbar-nav'>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/home']">
<span class='glyphicon glyphicon-home'></span> Home
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/counter']">
<span class='glyphicon glyphicon-education'></span> Counter
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/fetch-data']">
<span class='glyphicon glyphicon-th-list'></span> Fetch data
</a>
</li>
</ul>
</div>
</div>
</div>

This is a standard Bootstrap nav-menu template, but it has a few cunning Angular directives embedded into it:

  • routerLink
  • routerLinkActive

I’ve highlighted their uses in the above code snippet

The routerLink directive takes an array of route names and a collection of query parameters, and these are parsed into full href tags.

The routerLinkActive directive is used to apply a class (or a number of classes) to an element when the page URL matches that of the routerLink URL.

For instance, if the user browses to the URL ‘/counter’ then the Counter li element in the following markup will have the ‘link-active’ class applied, and all other li elements will not

<div class='navbar-collapse collapse'>
<ul class='nav navbar-nav'>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/home']">
<span class='glyphicon glyphicon-home'></span> Home
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/counter']">
<span class='glyphicon glyphicon-education'></span> Counter
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/fetch-data']">
<span class='glyphicon glyphicon-th-list'></span> Fetch data
</a>
</li>
</ul>
</div>
</div>

I’ve highlighted the lines which cause this to happen

Conclusion

In this post we’ve created an Angular2 Single Page Application from the suite of .NET Core SPA templates and done a deep dive into how .NET Core and Angular2 work together to make this template useful.

Have you built anything with the Angular2 template yet? In next week’s post, I’m going to be walking through how I made a UI for the webApi project that I made in the webapi-tutorial series.

If this post has helped you out, please consider   Buy me a coffeeBuying me a coffee
Jamie Taylor
A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about video games (which he runs with his brother)