Translating Angular 2 with ASP.NET MVC

Angular 2 uses templates for the html. I would like to write my templates in Razor and have them processed on the server before they get to the client side. This way I can use resource files for translations.

Note: See my other blog post ‘Translating ASP.NET MVC with resource files’ for an explanation on how to use resource files and how to set the language on the server.

The core concept

The concept is simple, on the server I create a controller which has an action for each template. And each action has the template in its corresponding View. In Angular we simply specify the URL for the action as the URL for the template.

namespace AngularApplication.Controllers
{
    public class TemplateController : Controller
    {
        public ActionResult Welcome()
        {
            return View();
        }

        public ActionResult Locale()
        {
            return View();
        }
    }
}
@using AngularApplication.Resources
<h2>@WelcomeTexts.Welcome</h2>

<p>
    @WelcomeTexts.Introduction
</p>
import { Component } from '@angular/core';

@Component({
    selector: 'angular-application',
    templateUrl: '/Template/Welcome'
})
export class AppComponent {}

Switching languages

To switch language I create an Angular component and an Angular service. The component handles the interface for the user and the service sends the chosen language back to the server. After the user selects a new language the Angular application is reloaded.

import {Component} from '@angular/core';
import {LocaleService} from './locale.service';

@Component({
    selector: 'locale-switcher',
    templateUrl: 'Template/Locale'
})

export class LocaleComponent {
    constructor(private _localeService: LocaleService) {}

    switch(language: string) {
        this._localeService.switchLanguage(language);
    }
}
@using AngularApplication.Resources
<h2>@LocaleTexts.SwitchLanguage</h2>

<ul>
	<li><a (click)='switch("en")'>English</a></li>
	<li><a (click)='switch("sv")'>Svenska</a></li>
</ul>
import {Injectable, ApplicationRef} from '@angular/core';
import {Http, Response} from '@angular/http';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class LocaleService {
    constructor(private _http: Http) {}

    switchLanguage(language: string) {
        this._http
            .post('Locale/Switch', { language: language })
            .subscribe();

        // Must reload to change language in templates
        window.location.reload();
    }
}

But wait, there’s a cache!

While the above sounds good on paper it won’t work terribly well in practice. The content of the templates depend on the language, but this isn’t reflected in the URLs. Hence any caching going on in the browser will interfere when the user changes the language. And any caching on the server will interfere when there are users with different languages.

To fix this we must add the language to the template URLs.

namespace AngularApplication.Controllers
{
    [OutputCache(Duration = 604800, VaryByParam = "language")]
    public class TemplateController : Controller
    {
        public ActionResult Welcome(string language)
        {
            return View();
        }

        public ActionResult Locale(string language)
        {
            return View();
        }
    }
}

The browser then has to use these new URLs when getting the templates. Therefore, we need to send the language to the browser. We do this by adding it to the DOM and using a bit of JavaScript to get it into a global JavaScript variable.

<div style="display:none">
    <div id="language" data-language="@ViewBag.Language"></div>
</div>
window.onload = function() {
    document.language = document
        .getElementById('language')
        .dataset['language'];
};

Lastly, we must add the language to the template URL in the component. We also have to add a type definition for document, otherwise TypeScript will complain about it being an unknown type.

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

declare var document: any;

@Component({
    selector: 'angular-application',
    templateUrl: `/Template/Welcome?language=${document.language}`
})
export class AppComponent {}

Let Angular do the work

From the old battlefield of JavaScript frameworks for single page applications, two seem to be emerging victorious. Angular and React. With the former being the more popular of the two and with Angular 2 on its way, it’s worth to take a look at.

<!DOCTYPE html>
<html>
<head>
  <title>Minimal one file Angular 2 example</title>
  <script src="node/core-js/client/shim.min.js"></script>
  <script src="node/zone.js/dist/zone.js"></script>
  <script src="node/reflect-metadata/Reflect.js"></script>
  <script src="node/rxjs/bundles/Rx.umd.js"></script>
  <script src="node/@angular/core/bundles/core.umd.js"></script>
  <script src="node/@angular/common/bundles/common.umd.js"></script>
  <script src="node/@angular/compiler/bundles/compiler.umd.js"></script>
  <script src="node/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
  <script src="node/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function() {
      ng.platformBrowserDynamic.bootstrap(ng.core
        .Component({
          selector: 'angular-app',
          template: '<h2>{{greeting}}</h2>'
        })
        .Class({
          constructor: function() {
            var thisClass = this;
            thisClass.greeting = 'Hello world';
            setTimeout(function() { thisClass.greeting = 'Cruel world' }, 1700);
          }
        }));
    });
  </script>
</head>
<body>
  <angular-app>Loading...</angular-app>
</body>
</html>

In practice Angular 2 consists of numerous JavaScript files compiled from TypeScript. Npm is used for getting these files to your computer. Which scripts you then use on your site depends on what you want to do. The set of scripts used in the example are taken from the official quickstart guide.

An Angular 2 application consists of a tree of components with one component at the top. The top component is then used to bootstrap the entire application. In the example there is only component defined directly into the bootstrap function.

Data in a web page

There are several places to store data in an ASP.NET web page. These places are far from interchangeable. Careless placement of data can lead to a poor web page and bad user experience. The following are some guide lines that I consider appropriate.

URL

Use for: Data that defines the web page. Such as the ID for a product or the terms for search results.

Remember: This becomes permanent if the page is bookmarked. Don’t store temporary modifiers in the URL. For example, if the user bookmarks the login page after a failed login attempt they shouldn’t have to see the error message every time they return.

DOM

Use for: Data that makes up the web page. Apart from the obvious, text and HTML, this can include JSON to be used by JavaScript.

Remember: Data attributes are a good place to store data for use in JavaScript.

Global JavaScript variables

Use for: Keeping state in single page applications or other long lived web pages with lots of JavaScript.

Remember: In JavaScript all scripts share the same namespace. Take care that your variables don’t clash with other scripts, for example by putting all your global variables into one global object. There are other ways to handle this as well.

Local storage

Use for: Applications where you can’t or don’t want to store data server side.

Remember: This data is stored permanently in the browser. The data won’t be available if the user returns with a different browser or device.

Cookie

Use for: Remembering users across visits on web sites requiring login. Or remembering user settings on web sites that does not use logins.

Remember: Current EU law dictates that web sites targeting EU citizens must obtain user permission when setting cookies. Though there are some exceptions.

Session

Use for: Data related to the currently logged in user.

Remember: The session is controlled by a session cookie stored in the browser. ASP.NET takes care of this automatically. It is the browser that determines when the session ends by removing the cookie. Session cookies used for authentication purposes does not fall under the EU law described above.

TempData

Use for: Data you want to keep between page requests, but not longer. Good for showing the user that data has been saved for example.

Remember: This data is stored in the session but has the extra property that it disappears after being read.

Application data

Use for: As a cache for common data needed server side.

Remember: This data is common for all users and persists until the application reloads. The data must be handled in a thread safe manner.

Database

Use for: Everything that needs permanent storage.

Remember: Database design and management is a whole science in and of itself.

SignalR: talk to your clients

In HTTP the browser is supposed to initiate all requests. This makes two way communication difficult. If the server wants to push data to the browser the browser has to poll for it. Possibly with the help of Server-sent events. There is a modern solution, however. It is called WebSocket. But even when it can be used all it provides is a low-level TCP connection.

SignalR takes care of both of these problems; It uses the best technology available and it provides a common easy-to-use interface regardless of the underlying connection. SignalR is intended to be used with ASP.NET and jQuery.

Counter: <span id="counter">-</span>
<button id="reset">Reset</button>

For this demonstration I want to have a counter that repeatedly counts up and a button to reset the it to zero. The magic is that the same number is to be displayed on all connected browsers.

<script src="~/Scripts/jquery.min.js"></script>
<script src="~/Scripts/jquery.signalR.min.js"></script>
<script src="~/signalr/hubs"></script>
[assembly: OwinStartup(typeof(SignalRTest.Startup))]

namespace SignalRTest
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

First there are some infrastructure on the browser and server that needs to be in place before SignalR can be used. The hubs script is created by SignalR, it does not exist physically.

$(function () {
    $.connection.counterHub.client.update =
        function (value) {
            $("#counter").html(value);
        };
    $("#reset").on("click", function () {
        $.connection.counterHub.server.reset();
    });
    $.connection.hub.start();
});

This is all the JavaScript necessary for this little demonstration. The first part defines an update function to be called from the server. The second part binds the button to a call to a reset method on the server.

public class CounterHub : Hub
{
    private static int value = 0;

    public void Reset()
    {
        Interlocked.Exchange(ref value, 0);
        Clients.All.writeTime(value);
    }

    public static void Increment()
    {
        Interlocked.Increment(ref value);
        IHubContext context = GlobalHost.ConnectionManager
            .GetHubContext<CounterHub>();
        context.Clients.All.update(value);
    }
}

On the server side is the reset method and a call to the update function. The update call is wrapped in a static method because it has to be called from outside the hub. This is the recommended way of doing it.

new Timer(o => CounterHub.Increment(), null, 0, 100);

I put this line in Application_Start in the Global.asax.cs file. It works for the demonstration. In a real scenario it should definitely be replaced with something more robust.

Trimming Handlebars with Razor

In JavaScript heavy web applications it is important to generate HTML in an organized way. This is an idea on how to accomplish this with the Handlebars template library, ASP.NET MVC and Razor views.

<div>
  <h2>{{name}}</h2>
  <ul>
    {{#each events}}
      <li>{{> event}}</li>
    {{/each}}
  </ul>
</div>
<strong>{{name}}</strong>

Each Handlebars template is put in a Razor file. By using Razor files we get some support from Visual Studio for writing the HTML. This also gives us access to the whole Razor engine, which could be used to handle localization among other things.

[OutputCache(Duration=604800)]
public ActionResult Compiled()
{
    string path = "~/Views/Templates/";

    var files = Directory.EnumerateFiles(Server.MapPath(path));
    var names = files.Select(f =>
        Path.GetFileNameWithoutExtension(f).ToLowerInvariant());

    var engine = new ScriptEngine();
    engine.ExecuteFile(Server.MapPath("~/Scripts/handlebars.js"));
    engine.Execute(@"var precompile = Handlebars.precompile;");

    var compiled = new StringBuilder(10240);
    compiled.Append("var templates = {");
    foreach (string name in names)
    {
        string file = path + name + ".cshtml";
        string template = RenderRazorViewToString(file);

        compiled.Append(name);
        compiled.Append(": Handlebars.template(");
        compiled.Append(engine.CallGlobalFunction("precompile", template));
        compiled.Append("),");
    }
    compiled.Append("};");

    foreach (string name in names)
    {
        compiled.AppendFormat(
            @"Handlebars.registerPartial(""{0}"", templates.{0});",
            name);
    }

    return Content(compiled.ToString(), "text/javascript");
}

Handlebars templates must be compiled before use. To increase performance we can compile them on the server, combine them and cache the result. To run Handlebars on the server we use Jurassic, an implementation of JavaScript for .NET. To get the templates from the Razor files see this implementation of RenderRazorViewToString.

The second foreach loop registers every template as a partial in order to use it from another template with the {{> name}} syntax. Only the templates used from other templates need to be registered but this just registers them all for simplicity.

<script src="~/Scripts/handlebars.runtime.js"></script>
<script src="~/Templates/Compiled"></script>

Since the templates are already compiled we only need the runtime version of Handlebars on the client. The precompiled templates are included like any other script.

(function () {
    var skiing = {
        name: 'Alpine',
        events: [
            { name: 'Downhill' },
            { name: 'Slalom' },
            { name: 'Super G' }
        ]
    };
    var body = document.getElementById('body');
    body.innerHTML = templates.sport(skiing);
})();

Using the templates is easy. Combine this system with a JavaScript framework and a REST resource and it could be something.

Organizing JavaScript with AMD

Asynchronous Module Definition is a standard for modularizing JavaScript code. RequireJS is one implementation of AMD.

define({
    pi: 3.14159,
    e: 2.71828
});

A module in AMD consists of a a value and an identifier. The identifier is the name of the file that contains the module minus the base URL and the file ending.

define(['app/constants'], function (constants) {
    return function (radius) {
        return {
            radius: radius,
            diameter: 2 * radius,
            circumference: 2 * constants.pi * radius,
            area: constants.pi * radius * radius
        };
    };
});

When a module is defined with a function that function will be called and the result will be used as the value. That value can still be a function which is useful because it can be used as a constructor.

This also shows how one module can depend other modules. AMD will take care of loading the other modules and pass their values to the function. Hence a function is required in this case.

require.config({
    baseUrl: '/Scripts',
    paths: {
        'jquery': 'http://code.jquery.com/jquery-2.1.0.min'
    }
});

require(['jquery', 'app/circle'], function ($, circle) {
    $('body').html(circle(5.3).area);
});

In the main JavaScript file RequireJS is configured and the application is started. Since this does not define a module it uses require instead of define.

<script src="/Scripts/lib/require.js" data-main="/Scripts/app/main"></script>

The only file that has to be included in the traditional way is the one for RequireJS. It then needs to know which file to start the application with.

The drawback of AMD is that there can only be one module per file. While this is good for organizing code during development it means a lot of files to download to the browser. AMD does load files asynchronously when needed. However, when used in production some way of combining JavaScript files is almost necessary.

Let the client do the work

Websites are increasingly built using Ajax and JavaScript. This has culminated in the single page application design. These are websites where the server is used only as a data store and all the heavy lifting is done in the client.

Writing this much client side code would be difficult without a good framework. Fortunately, there are many to choose from. Unfortunately, there are many to choose from. Most, if not all, of them are built on MV* principles, where the * stands for the fact that they often deviate from the MVC pattern.

Backbone.js

$(function () {
    var MainModel = Backbone.Model.extend({
        defaults: {
            greeting: 'Hello world'
        }
    });

    var MainView = Backbone.View.extend({
        tagName: 'h2',
        initialize: function () {
            this.model.on('change', this.render, this);
            this.render();
        },
        render: function () {
            this.$el.html(this.model.get('greeting'));
            $('body').html(this.$el);
        }
    });

    var AppRouter = Backbone.Router.extend({
        routes: {
            '': 'main'
        },
        main: function () {
            var model = new MainModel();
            var view = new MainView({ model: model });
            setTimeout(function () {
                model.set('greeting', 'Cruel world');
            }, 1700);
        }
    });

    new AppRouter();
    Backbone.history.start();
});

Backbone is one of the more popular frameworks. This example shows how models, views and routes are used to assemble the application. In addition there are collections which handle collections of models.

While some frameworks are very rigid in their structure Backbone is rather more flexible. There are some recommendations though. Use Underscore.js templates when rendering views. Use REST when getting data from the server. And you probably want to include jQuery even though the documentation likes to consider it optional.

There are plenty of extensions to Backbone. Most of which can be served from cdnjs together with Backbone itself and its dependencies.

Prototype: in the shadow of jQuery

Prototype is a rival to jQuery. Not that there is much contest. jQuery is in much wider use and much more actively developed. Still, Prototype does exist and similar things to what jQuery does.

The syntax and usage of Prototype show both similarities and differences to jQuery. You can compare the following pieces of Prototype code to what I wrote about jQuery in an earlier blog post. Before we begin using Prototype to manipulate the DOM we need to make sure it is loaded.

document.observe('dom:loaded', function ()
{
    // The DOM has been loaded
});

There are two element selectors in Prototype. The first only selects based on ID. The second uses CSS syntax.

$("bottom");        // by ID
$$("hgroup");       // by tag
$$(".ticket");      // by class
$$("em:empty");     // by pseudo class
$$("h2[title]");    // by tag, has attribute
$$("img[alt='']");  // by tag, has attribute value
$$("a.dead:empty"); // combination of selectors

The ID selector will always refer to a single element, since IDs are unique. The CSS selector will always give back an array of elements. To apply some function to them we have to iterate over them or apply a map.

$$(".warning").each(function (element) {
    element.insert({ before: "Warning: " });
});

$$(".error").invoke("insert", { before: "Error: " });

For the rest of the Prototype’s functionality I refer you to the Prototype API documentation.

Ajax via Prototype

Of course Prototype can do Ajax as well.

$("button").observe("click", function () {
	$("output").innerHTML = "processing...";
	new Ajax.Request("Page.aspx/Uppercase", {
	    method: "POST",
	    contentType: "application/json",
	    encoding: "utf-8",
	    postBody: Object.toJSON({ text: $("input").value }),
	    onSuccess: function (response) {
	        $("output").innerHTML = response.responseJSON.d.Text;
	    }
	});
});

This is the Prototype version of the client side code when calling a web method in ASP.NET Web Forms as I described in another blog post.

Location aware web sites with Geolocation

The Geolocation API gives us access to the user’s position from JavaScript. When called the browser will ask the user for permission, retrieve the position from GPS, Wi-Fi, IP address or anything else it has available and return it. The API is supported in all current browsers. Just be aware that Internet Explorer require you to use a proper doctype. Note that the Geolocation API is not part of HTML5 even though it is often discussed alongside it.

I have created an example page where you can see how it works from a user perspective and see what your browser returns.

The geolocation object

Before you can use geolocation you must make sure that the browser supports it.

if (navigator.geolocation) {
  // Geolocation is supported
}
else {
  // Geolocation is NOT supported
}

Then you can ask for the user’s position with the following call.

navigator.geolocation.getCurrentPosition(
  function (position) {
    // Use the position data
  },
  function (error) {
    switch (error.code) {
      case 1:
        // Permission was denied
        break;
      case 2:
        // Position was not available
        break;
      case 3:
        // Position request timed out
        break;
      default:
        // Unknown error
        break;
    }
  },
  { enableHighAccuracy: true, maximumAge: 0, timeout: 60000 });

This asks for a high accuracy position fix that has not been cached with a timeout of one minute. We will look at what is returned in the position object soon. If you need to continuously update the user’s position you can use the following call instead.

var watchId = navigator.geolocation.watchPosition(
  function (position) {
    // Use the position data
  });

// When you are done
navigator.geolocation.clearWatch(watchId);

The watchPosition method takes the same parameters as the getCurrentPosition method. Here I have only given it a function to call on success which is the only argument that is required (for both methods). With the watchPosition method the browser will call our function whenever the position changes until we tell it to stop.

The position object

The position object that is returned has the following data.

position.timestamp;               // Milliseconds since 1 January 1970
position.coords.latitude;         // Decimal degrees (WGS84)
position.coords.longitude;        // Decimal degrees (WGS84)
position.coords.accuracy;         // Decimal meters, 95% confidence level
position.coords.altitude;         // Decimal meters above the WGS84 ellipsoid
position.coords.altitudeAccuracy; // Decimal meters, 95% confidence level
position.coords.speed;            // Decimal meters per second
position.coords.heading;          // Decimal degrees from true north clockwise

All decimal values are of type double. Only the timestamp, latitude, longitude and accuracy are guaranteed to be returned. But if the altitude is returned then the altitude accuracy must also be present. This is according to the specification, real browsers may behave otherwise. Values that are not provided may be 0, NaN or null.

Gecko browsers (i.e. Firefox) will also return the user’s address.

position.address.premises;
position.address.street;
position.address.streetNumber;
position.address.postalCode;
position.address.city;
position.address.region;
position.address.county;
position.address.country;
position.address.countryCode;

All address values are of type string.

Server-sent events

Server-sent events is a new technology that allows the web server to send data to the browser. It works by adding an event source in JavaScript that calls a special page on the server. This page never returns but sends the events as text to the browser. If the page does return the browser will re-establish contact within a couple of seconds.

public EmptyResult Events()
{
    Response.ContentType = "text/event-stream";
    while (true)
    {
        string time = DateTime.Now.ToString();
        Response.Write("data: " + time + "\n");
        Response.Write("\n");
        Response.Flush();
        Thread.Sleep(5000);
    }
}

I used MVC for this test. After you set the content type correctly you can send event data by the syntax shown above. You can use several data: lines if you need to send multiline data. Each event is followed by an empty line. If you need to send different types of events you can name them by using an event: line. Unnamed events has the generic name message.

var events = new EventSource("events");
events.addEventListener("message", function (event) {
    alert(event.data);
}, false);

In the client we add an event source to the page URL (in my case event) and then add an event listener to it. Here we can add different listeners to different named events as described above.

Compared to WebSockets this technology doesn’t allow two-way communication. But in return it is much easier to implement. Browser support for server-sent events exist in the latest browsers except Internet Explorer 9. Unfortunately, there is no workaround for older browsers as far as I know.