Angular and the Server

Angular and the Server Header

Derived from photo by jeanbaptisteparis / flickr.com, CC BY-SA

Coming from an ASP.NET background, developers are familiar with the File, New Project… experience. This is the process to setup a new project in Visual Studio – typically from a template to help you get started effectively.

These starter projects include everything in the box. They have UI rendering, authentication, sample data APIs, and more. And while they are great for getting started, they may not be the best design for the application you are building.

When it comes to building an Angular application, how should it integrate with the server-side application if at all? What design options are available to you and how do you know which is the right one? This is an often-overlooked topic in Angular-focused material. One the other end, server-side articles tend to encourage using server-side features without considering that you are using a very powerful front-end framework already.

This article details several application responsibilities and groups them in terms of their integration with the host application. For the purposes of this article, the host application is the server-side application that sits beneath your Angular application and serves it to the browser/client. (Spoiler alert, there might be NO application sitting beneath your Angular application.) The Angular application consists of the TypeScript and/or JavaScript you write against the Angular framework.

Level 0 – No Server Application

This level includes features that are configurable to have no server-side dependency. This means that when the application runs, Angular handles these features without requiring the server to process any of the logic. This configuration may be desirable especially when static file hosting is the preferred deployment option.

Even though there is no run-time server dependency, you can still leverage the power of a build to optimize your application. These are some of the features you can configure with no server-side backend.

Routing

The Angular router is optionally configured to use the hash URL style. In this configuration, each route is represented in the URL following the hash symbol, like example.com/#/about or example.com/#/products/1. The main benefit is that when someone enters this URL into the browser, they are going to the same endpoint each time, example.com. When the application loads, the router kicks in and navigates to the correct view.

Internationalization (i18n)

Angular includes utilities to internationalize applications. There are several phases to this process which you can read about in the documentation. At the end, you are left with multiple versions of your application that are deployed to their own directory as static files. At this point, there are several options for routing the user to the correct version of the application. The choice can be left to the user in the UI or the application can read the language settings in the browser to automatically route to the correct locale.

View Rendering

One of the first highlights of the Angular framework was its rendering. Being able to bind a JavaScript object to a template and see the data and events wire themselves up was almost magic. Angular accomplishes this in the browser and while server frameworks have their own rendering engines, you can generally rely on client-side rendering for your views when using Angular.

Level 1 – Host Application Integration

This level consists of functionality where the host application and the Angular application work together to produce the runtime functionality. While the server may not know intimately about the Angular application, both the server and client must be configured in a complimentary manner and make assumptions about the other's behavior.

Routing

As opposed to the hash style URL discussed earlier, the more common HTML 5 pushState style URL depends on server support. These URLs lack the hash symbol, for example example.com/about or example.com/products/1. The issue here is that when someone navigates to your application the first time using one of these URLs, the Angular application doesn't have a chance to handle the route. Instead, the server receives the request first and then returns the Angular application to the client to then complete the routing process. You can see an example with ASP.NET Core here.

Authentication

Sometimes, there are reasons to lock down your Angular application to only authorized users. While the Angular application could handle this based off of Web API authentication, you also have the option to authorize with the host application. For instance, the first time someone navigates to your web application, the server can return the 'not authorized' messaging without returning any of the Angular code to the browser.

View Rendering

As mentioned previously, Angular's rendering is more than capable of generating your application's UI. Generally, this rendering occurs in the browser. However, applications optimized for SEO and time-to-load performance, benefit from server-side rendering.

This is where Angular Universal fits in. Instead of using the server framework's view engine, Angular Universal renders Angular templates server-side. The browser receives the HTML and CSS to display the page immediately without client-side rendering. Subsequent views are loaded via AJAX and rendered on the client to reduce subsequent payload sizes. The framework supports both Node.js and ASP.NET Core backends.

Using Angular Universal does increase complexity and imposes certain restrictions in how you write your Angular application. Be sure that if you need this extra performance boost that you understand the trade-offs. Read more about it on GitHub.

Internationalization

Internationalization is an area where the host application may not have a direct role but may need to assist integrated routing, authentication, and/or rendering to provide the desired experience.

Logging

Logging also doesn't necessarily represent a tight integration between the host application and the Angular application. However, consider that as you increase the server's role with routing, authentication, and/or rendering, you should consider logging any errors resulting from this increased responsibility.

Level 2 – Web APIs

These dependencies are server-side dependencies. However, they do not have to live within the same web application hosting the Angular application. By keeping these dependencies in their own code base, there are many advantages. Builds and deployments are performed only for the applications that have changed. The APIs are built with whichever technology the team and/or company decide so long as it works over a common protocol – typically HTTPS. If you are familiar with the concepts of Web APIs and/or Microservices, this group encompasses those concepts.

Web APIs

Some examples of web API responsibilities that an application requires include data access, logging, and usage analytics. Interestingly, none of these concerns require any knowledge of the Angular application. In fact, they could serve many different front-end applications.

Authentication

Authentication at this level is typically handled directly between the Angular application client and the Web API server. There are many ways to do this but today's solutions generally use some form of JSON web tokens to maintain the user's identity.

Internationalization

At this level, any assets requiring i18n would be handled by the web API using server-side techniques. Again, this has no coupling to the Angular application beyond the API contract.

A Case for Bundling

Sometimes, applications are small and the API that they access is small. In this case, you may decide to keep this functionality together in the same application. They share the same build and the same deployment. Sometimes the effort to split these functions isn't greater than the payoff.

In this case, consider your development and design carefully as to avoid creating unnecessary coupling between the concerns. The web API may one day 'grow up' to need its own project so develop accordingly.

Final Thoughts

Every application is different. Hopefully by reading about how application concerns create dependencies between the server and the Angular application, you can apply these considerations to your own architecture.

One thing you might have noticed, there's no discussion of development tools that integrate with the server. This is a topic for a future post so stay tuned.

Ultimately, there is no one way to design an application. However, by knowing your options, you're better equipped to make lasting design decisions for your project.

What do you think? Is there functionality you prefer to run on the server? The client? Please share in the comments.

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

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

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

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

This article discusses an important part of the integration between client and server – routing. Angular 2 has its own router and so does ASP.NET Core. In the following sections you learn to setup routing in Angular 2 and then how to support the Angular 2 routing with ASP.NET Core.

This post addresses the high-level concerns of integrating these two routing systems. For more information on the individual routing features for each framework, read about Angular 2 routing here and ASP.NET Core routing here.

.NET Core SDK Preview 3: This tutorial uses .NET Core SDK Preview 3 which is available on GitHub. This version includes 'alpha' support for MSBuild-based projects that use .csproj configuration instead of project.json configuration. Microsoft announced that MSBuild is the common project configuration for .NET Core based projects going forward. By using MSBuild, the goal is to better prepare you for future versions of the tooling.

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

Refactor to Multiple Components

In order to implement routing for Angular 2, you need to have multiple components in the project.

Move the contents of AppComponent to a new component called PairingComponent which is responsible for pairing Sriracha with foods.

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

@Component({
    moduleId: module.id,
    selector: 'my-pairing',    
    template: `
        <h2>Pairing</h2>
        <input type="text" [value]="food" (input)="foodInput($event)"/>        
        <p>Sriracha sauce is great with {{ food }}</p>
    `,
})
export class PairingComponent { 
    food = 'kielbasa';

    foodInput(event: Event) {
        const target = event.target as HTMLInputElement;
        this.food = target.value;
    }
}

The AppComponent becomes the application shell including the navigation. For now, the PairingComponent is rendered directly in the AppComponent template.

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

@Component({
    moduleId: module.id,
    selector: 'my-app',    
    template: `
        <nav>
            <a href="/pairing">Pairing</a> | 
            <a href="/about">About</a>            
        </nav>
        <my-pairing></my-pairing>
    `,
})
export class AppComponent { }

Also, create a third component called AboutComponent which serves as a second view.

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

@Component({
    moduleId: module.id,
    selector: 'my-about',    
    template: `
        <h2>About Sriracha</h2>

        <p>This is what Wikipedia says about <a href="https://en.wikipedia.org/wiki/Sriracha_sauce_%28Huy_Fong_Foods%29">Sriracha sauce</a>:</p>

        <blockquote>It can be recognized by its bright red color and its packaging: a clear plastic bottle with a green cap, text in Vietnamese, English, Chinese, French, and Spanish, and the rooster logo. David Tran was born in 1945, the Year of the Rooster...<blockquote>
    `,
})
export class AboutComponent { }

Finally, you declare all of these components in the AppModule.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { PairingComponent } from './pairing.component';
import { AboutComponent } from './about.component';

@NgModule({
    bootstrap: [ AppComponent ],
    imports: [ 
        BrowserModule,
    ],
    declarations: [ 
        AppComponent, 
        PairingComponent, 
        AboutComponent, 
    ],
})
export class AppModule { }

The app directory now looks like this:

component folder structure

Run your application to see the new navigation.

Navigation Screenshot

Configure the Angular 2 Router

To configure the AppModule routes, import RouterModule and Routes from @angular/router. The Routes type is an array of Route objects used to define the routes.

When navigating to http://localhost:5000/pairing the application should show the PairingComponent. When navigation to http://localhost:5000/about the application should show the AboutComponent. There is also a third scenario. When navigating to the root of the application, http://localhost:5000/, the application should redirect to http://localhost:5000/pairing as the default.

This is what the Routes array looks like:

const routes: Routes = [
  { path: '', redirectTo: '/pairing', pathMatch: 'full' },
  { path: 'pairing',  component: PairingComponent },
  { path: 'about', component: AboutComponent },
];

The pathMatch property must be set for all redirects. In this case, the value 'full' denotes that the redirect should occur when the path is at the application root or is otherwise empty ('').

Next, register these routes with the RouterModule and register the RouterModule with the AppModule.

This is the final code for the AppModule:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, Routes } from '@angular/router';

import { AppComponent } from './app.component';
import { PairingComponent } from './pairing.component';
import { AboutComponent } from './about.component';

const routes: Routes = [
  { path: '', redirectTo: '/pairing', pathMatch: 'full' },
  { path: 'pairing',  component: PairingComponent },
  { path: 'about', component: AboutComponent },
];

@NgModule({
    bootstrap: [ AppComponent ],
    imports: [ 
        BrowserModule,
        RouterModule.forRoot(routes),
    ],
    declarations: [ 
        AppComponent, 
        PairingComponent, 
        AboutComponent, 
    ],
})
export class AppModule { }

Finally, the Angular router depends on a base element with href defined. You add this in the head of index.html so that Angular can update the browser's address bar correctly. This is the updated index.html code:

<!DOCTYPE html>
<html>

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

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

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

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

<body>
    <my-app>Loading...</my-app>
</body>

</html>

Configure the Navigation

The last step to configure the Angular routing is to configure the navigation to utilize the routes. To accomplish this, you modify the AppComponent template in two significant ways.

First, instead of including the my-pairing element in the AppComponent template, replace it with the router-outlet element. This is a placeholder where the router renders the component matching the current route.

Finally, you replace the href attribute with the routerLink attribute. This is a directive instructing Angular to register the click with the router instead of the default href behavior.

The final code for the AppComponent looks like this:

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

@Component({
    moduleId: module.id,
    selector: 'my-app',    
    template: `
        <nav>
            <a routerLink="/pairing">Pairing</a> | 
            <a routerLink="/about">About</a>            
        </nav>
        <router-outlet></router-outlet>
    `,
})
export class AppComponent { }

At this point, run the application using Ctrl+Shift+B and start the browser using npm run chrome which you configured in part 5. Notice how the application defaults to the /pairing URL. Clicking on the link loads the appropriate component for that specific URL.

Working Angular 2 Routes

There's still one problem. When you go directly to a route URL like http://localhost:5000/about from the browser's address bar, you see a 404 error. This is a result of the fact that even though Angular knows how to reconcile this URL, the server does not.

In the next section, you configure the ASP.NET MVC routes to return the Angular application when it doesn't recognize the route.

Add ASP.NET Routing

In order to configure ASP.NET routing, you must install the ASP.NET MVC package. Add this PackageReference to the .csproj file and then run dotnet restore from the terminal.

<PackageReference Include="Microsoft.AspNetCore.Mvc">
    <Version>1.1.0</Version>
</PackageReference>

Once the package is installed, create a directory at the root of your project called Controllers. Inside this directory, create a file called HomeController.cs. In this file, create a HomeController class which inherits Controller. This is the code.

using System.IO;
using Microsoft.AspNetCore.Mvc;

public class HomeController : Controller
{
    public IActionResult Index()
    {
        var fileName = "index.html";
        var contentType = "text/html";

        string filePath = Path.Combine(Directory.GetCurrentDirectory(), fileName);
        string fileContents = System.IO.File.ReadAllText(filePath);

        return Content(fileContents, contentType);
    }
}

In ASP.NET MVC, controllers are where a request is processed and a response is constructed. In this controller, the Index method reads the index.html file containing the Angular application and returns the file contents as a text/html response to the browser. This is essentially the same response that the browser receives when requesting index.html directly. Once the application is loaded into the browser, the Angular router takes over.

In order for the HomeController to do its thing, you must configure the ASP.NET MVC router to direct all of the incoming requests to the Index method.

In the Startup.cs file, you have to add a couple new configurations. First, ASP.NET MVC is configured as a service which requires the Startup class to implement a ConfigureServices method. Also, you configure the routes in the existing Configure method. The new Startup.cs code looks like this:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseDefaultFiles();
        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{*url}",
                defaults: new { controller = "Home", action = "Index" });
        });
    }
}

The MapRoute method takes a configuration object. The template field defines the URL pattern that matches the route. Because there is only one variable defined in the template and it uses the asterisk, this route essentially handles all requests that haven't already been handled by the UseDefaultFiles or UseStaticFiles middleware. Furthermore, the defaults field directs all of these requests to the HomeController and it's Index action method.

At this point, run your application. In the browser, go directly to http://localhost:5000/about and you will see that the application loads from the server. Once the Angular application loads, it applies the correct route on the client.

Routing Integration

The End

This brings the final part of this Angular 2, ASP.NET Core, and Visual Studio Code journey to a close. You have learned a lot about configuring these frameworks and tools to work together. You've learned your way around Visual Studio code. You configured an Angular 2 and ASP.NET Core application so that both frameworks play well together. You created a basic Angular 2 application and automated tasks with npm and Visual Studio Code.

But you have only scratched the surface. This is just the beginning. What was your favorite discovery? Which topics are you most interested in learning about more? Leave a comment below and have fun with your new tools!