Progressive Enhancement Enhanced

Every developer who listens to their heart will agree that progressive enhancement is the right way to build web apps. It’s the only development approach that embraces the idea that web content should be accessible to all users, regardless of browser or network. So why isn’t Progressive Enhancement, or PE, more popular?

For PE to be a viable approach, it must produce an app that’s easy to enhance. Because, without the enhancements, the app will be sluggish for all user interactions other than the initial page load. But traditional PE, by starting with the server rendered version, produces an app that’s too hard to enhance. With no client side abstraction to fall back on, we’re forced into writing messy DOM manipulation code.

With React, there’s a new way to do PE. We start with a basic client rendered version and then use React’s isomorphic capabilities to render this same app on the server. The end result is an app that’s easy to enhance because we have a React Component hierarchy on the client to interact with instead of the DOM.

All the examples I’ve seen use Node for the web server. To redress the imbalance, I’ve built an example using ASP.NET. It’s a typical master/details scenario consisting of two views. The first displays a list of people and the second an individual person’s details. In keeping with the new approach to PE, let’s start with the client rendering.

Client Rendering

First come the React Components, one for each view. We’ll keep the rendering of our Components synchronous because it will simplify matters when we come to the server rendering. That means keeping the Components free of data access code. Here’s what our Details Component looks like, where the selected person is passed in fully populated:

var Details = React.createClass({
    render: function() {
        var person = this.props.person;
        return (
            <div>
                <h2>{person.Name}</h2>
                <div>Date of Birth</div>
                <div>{person.DateOfBirth}</div>
            </div>
        );
    }
});

With this new approach to PE, the role of the JavaScript router cannot be overstated. It will be the making or breaking of our app. For reasons that will become apparent as you read on, we’re using the Navigation router. Here’s the Navigation router configuration for the two routes in our example master/details app:

var stateNavigator = new Navigation.StateNavigator([
    {key: 'people', route: ''},
    {key: 'person', route: 'person/{id}'}
]);

The configuration is made up of States. There’s a ‘people’ State and a ‘person’ State, one for each view. Here’s how to build the Hyperlink that selects a person, where the stateKey refers to the key of the destination State and the navigationData holds the route data:

<NavigationLink 
    stateKey="person"
    navigationData={{id: person.Id}}
    stateNavigator={stateNavigator}>
    {person.Name}
</NavigationLink>

We want to display the Details Component when this Hyperlink is clicked. We’ll do this by attaching a navigated function to the ‘person’ State. The Navigation router will automatically call this function whenever the ‘person’ State is navigated to. Inside this function we’ll ask React to render the Details Component into the content placeholder in our HTML:

var states = stateNavigator.states;
states.person.navigated = function() {
    React.render(<Details />, 
        document.getElementById('content'));
}

For the render to work, we must pass the selected person’s details as props to the Component. We need an ASP.NET JSON Web Api method so we can retrieve these details over Ajax:

public class PersonController : ApiController
{
    public Person Get(int id)
    {
        return new PersonRepository()
                       .People.First(p => p.Id == id);
    }
}

By creating a JSON Web Api we’ve inadvertently created two sets of routes. One set is held on the client for the Navigation router and the other is held on the server for the Web Api. Let’s do away with the separate Web Api configuration by reusing our client routes on the server.

Route Reuse

Routes in the Web Api are used to map requests to Controllers. So, if we plan to get rid of them, we’re going to need to replace the default Controller lookup mechanism. Instead, our custom Controller selector will use the Navigation router to do the request to Controller lookup. The Navigation router lets us map Urls to States by passing the requested Url into the start function:

stateNavigator.start(url);
var state = stateNavigator.stateContext.state;

To simplify the mapping, we’ve twinned the names of the Controllers to the names of the States. For example, a Url of ‘person/2’ returns the ‘person’ State which maps to the PersonController.

To run the Navigation router on the server, our JavaScript must be written inside Node using npm. This doesn’t mean duplicating our JavaScript because we’ll use browserify and gulp to bundle it up for use on the client. We’ll call into Node from C# using Edge.js, a library that lets us script Node in process.

Edge.js accepts a string containing the Node code and the JavaScript function to execute. Data is received from C# via the function’s first parameter and passed back from Node via its second callback parameter. Here is the C# getContext Func that calls Node passing in a Url and returns the matching State key:

Func<object, Task<object>> getContext = Edge.Func(@"
    var Navigation = require('navigation');
    var App = require('App');
 
    var stateNavigator = App.createStateNavigator();
    return function (url, callback) {
        stateNavigator.start(url);
        callback(null, stateNavigator.stateContext.state.key);
    }
");

Calls to Edge.js are async. In the Web Api, Message Handlers are the place to make async calls. We’ll create a ContextHandler Message Handler and override its SendAsync method passing the requested Url into our getContext Func. We’ll store the key returned in the request’s Properties collection so it can be accessed downstream by our custom Controller selector:

protected async override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, 
    CancellationToken token)
{
    var url = request.RequestUri.PathAndQuery;
    var key = (string) await getContext(url);
    request.Properties["key"] = key;
    return await base.SendAsync(request, token);
}

We’ll build our Controller lookup mechanism by creating a class that implements IHttpControllerSelector. It’ll use the State key from the request Properties to return the associated Controller Type from the SelectController method:

public HttpControllerDescriptor SelectController(
    HttpRequestMessage request)
{
    return new HttpControllerDescriptor
    {
        Configuration = request.GetConfiguration(),
        ControllerType = Type.GetType(
            "NavigationEdgeApi.Controllers."
            + request.Properties["key"]
            + "Controller", true, true)
    };
}

In WebApiConfig, we’ll register our Message Handler and replace the default Controller selector with our new one:

config.MessageHandlers.Add(new ContextHandler());
config.Services.Replace(typeof(IHttpControllerSelector), 
    new ControllerSelector());

With our Controller selector in place, we’ll return to the client. Clicking the Hyperlink to select a person with id 2 from the list navigates to a Url of ‘person/2’. An Ajax call to our JSON Api passing this Url will return that person’s details because the Urls on the client and server now match.

We’ll place this Ajax request inside a navigating function attached to the ‘person’ State. The Navigation router will automatically call this function before the ‘person’ State is navigated to, giving us a chance to suspend the navigation until the Ajax call returns:

states.person.navigating = function(data, url, navigate) {
    getJSON(url, function(resp) {
        navigate({ person: resp });
    });
}

Our navigated function attached to the ‘person’ State is passed the JSON data in its second parameter. We’ll hand on the selected person as props to the Details Component:

states.person.navigated = function(data, asyncData) {
    React.render(<Details person={asynctData.person} />, 
        document.getElementById('content'));
}

We’ve got our Client rendering working with just a single set of routes. We don’t have a separate set of routes for the JSON Api. Not only does this make the code simpler, but it smooths the path for the server rendering.

Server Rendering

Imagine that, rather than left clicking the person selection Hyperlink, we right clicked and opened it in a new tab. The Url requested would still be ‘person/2’ but we must return the person’s details as HTML instead of JSON. Rather than cluttering up our Controllers with conditional logic, we’ll use content negotiation to return different responses based on the content type requested.

To avoid duplicating code, we’ll reuse our Details Component on the server to render the HTML. To help locate the Details Component based on the incoming request, we’ll add it to the ‘person’ State of our Navigation router configuration:

{key: 'person', route: 'person/{id}', component: Details}

Then we’ll use the Navigation router to look up the Component for a given Url, much like we did for our Controller lookup mechanism:

stateNavigator.start(url);
var component = stateNavigator.stateContext.state.component;

We’ll use Edge.js again to create a render Func that returns the HTML for a given Url. To perform the rendering for ‘person/2’, this Func needs the person JSON data so it can build the props to pass to the Details Component. Once the Component’s built, it can return the HTML via a call to React’s renderToString function:

Func<object, Task<object>> render = Edge.Func(@"
    var React = require('react');
    var Navigation = require('navigation');
    var App = require('App');
                 
    var stateNavigator = App.createStateNavigator();
    return function (data, callback) {
        stateNavigator.start(data.url);
        var props = {};
        props[stateNavigator.stateContext.state.key] = data.item;
        var component = React.createElement(
            stateNavigator.stateContext.state.component, props);
        callback(null, React.renderToString(component));
    }
");

We’ll create a RenderHandler Message Handler that intercepts the JSON response. If the request originated from an Ajax call then it lets the response continue unchanged. Otherwise, it passes the JSON into our render Func and overwrites the response with the HTML returned.

protected async override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, 
    CancellationToken token)
{
    var response = await base.SendAsync(request, token);
    if (request.Content.Headers.ContentType == null)
    {
        var html = (string) await render(new { 
            url = request.RequestUri.PathAndQuery, 
            item = ((ObjectContent) response.Content).Value
        });
        response.Content = new StringContent(
            "<div id='content'>" + html + "</div>");
        response.Content.Headers.ContentType 
            = new MediaTypeHeaderValue("text/html");
    }
    return response;
}

The last piece of this PE puzzle is to get the server and client rendering working together. After the HTML is returned we want React to take over on the client so that subsequent Hyperlink clicks result in Ajax requests. By sending the props along with the server rendered HTML, we can trigger a client render after the page loads. This allows React to catch up with the server rendered content.

TL;DR

With isomorphic React, there’s a new way to do Progressive Enhancement that blows the old way out of the water. I’ve built an example using ASP.NET that shows this approach is open to all developers, not just those using Node for their web server. There’s not an ounce of duplicated code. Even the routes and JSON Api are shared across the client and server rendering.

Advertisements

The JavaScript Router that’s got Baked in Breadcrumbs

Hansel is the fairytale boy who left a trail of breadcrumbs in his wake so that he and sister Gretel could find their way out after being abandoned in the woods. Very forward and backward thinking of him. If Hansel and Gretel were navigating a website rather than a woods, the breadcrumbs become a set of Hyperlinks signposting their journey. The Navigation router is the first JavaScript router to dynamically track a user’s path through a website, dropping Hyperlink breadcrumbs as they go.

With other routers, you might’ve resorted to manipulating browser history. But using history.go(-1) is like forcing Hansel and Gretel to walk backwards along the trail and, because browser history can’t be interrogated, might lead the unsuspecting travellers into an entirely different woods. If you manually tracked the path, instead, your Hyperlinks might disappear when the browser is refreshed, much as Hansel’s breadcrumbs disappeared when set upon by birds.

The Navigation router stores its breadcrumb trail in the Url, so it won’t be eaten when the page is refreshed or opened in a new tab. You can see the breadcrumb trail in action in Angular, Knockout or React. These examples display a list of people. Selecting a person takes you through to their details and drops a Hyperlink breadcrumb that returns you back to the list of people.

A New Kind Of JavaScript Router

Until now, you only had two kinds of routers to choose from: lazy routers and overbearing routers. A lazy router does little more than relay requests to methods, but has the temerity to leave the hard work of hyperlink construction up to you. An overbearing router will do the jobs a lazy router won’t touch, but only if you agree to its choice of UI library and if you give it the final say on how your views are loaded. Now, there’s a new kind of router to choose from: the diligent and unassuming Navigation router.

The Navigation router doesn’t mind whatever UI library you choose to use. That isn’t to say it doesn’t care, because it works hard to accommodate your choice. It provides custom Angular directives, Knockout bindings or React components so you can create hyperlinks in native UI syntax. These hyperlinks go above and beyond what even the most conscientious of overbearing routers can provide. To demonstrate, we’ll spend the rest of this article building a mini replica of the smashingmagazine.com website as a Single Page Application.

Listing The Articles

The smashingmagazine.com website is a great one to replicate because it has plenty of different types of hyperlinks to get our teeth into: main menus, category filters, pagination and article selection. Each type will illustrate a different aspect of the Navigation router. Let’s start with the main menu hyperlinks.

Listing the Articles

Before we can build these hyperlinks, we must configure the Navigation router with a list of States. Each State represents a different view within the website. Clicking the Smashing Magazine logo takes us to a list of articles, so let’s create a State representing this view.

var articleDialog = {
    key: 'article', initial: 'list', states: [
        // The list of articles State
        { key: 'list', route: '' }
    ]
};

We’ve assigned the State an empty route because it represents the Smashing Magazine home page. Although the State only consists of a key and a route, it must be wrapped inside a Dialog. Dialogs group related States together. When we come to add the hyperlinks for the article selection, this Dialog will gain another State.

The second menu hyperlink displays a list of books. To represent this view, we’ll create a State with a ‘books’ route and wrap it in a new Dialog.

var bookDialog = {
    key: 'book', initial: 'list', states: [
        { key: 'list', route: 'books' }
    ]
};

We could continue configuring States for the remaining four menu items but, for the sake of this demo, the two States we have already are enough. The configuration is completed by passing the Navigation router an array containing the two Dialogs.

Navigation.StateInfoConfig.build([ articleDialog, bookDialog ]);

It’s time to create the hyperlinks. Make a note of the ‘article’ and ‘book’ Dialog keys because we can’t define the hyperlinks without them. The hyperlink syntax is dependent on your choice of UI library. If you’re using Angular, you can hyperlink to the articles view using the navigation-link custom directive.

<a navigation-link="article">Articles</a>

That same hyperlink in Knockout is created using the navigationLink custom binding.

<a data-bind="navigationLink: 'article'">Articles</a>

And in React, it’s the turn of the NavigationLink custom component.

<NavigationLink action="article">Articles</NavigationLink>

There’s a number of code snippets to come and, rather than writing each one out three times (for Angular, Knockout and React), they’ll be written just the once in Knockout. Even if you’re unfamiliar with Knockout, you should still be able to make sense of the snippets. But don’t give up if you’re struggling, at the end of each section are links to CodePens containing the equivalent code written in Angular and React.

Let’s flesh out the HTML by adding a list of articles below the top level menu items. We’ll add a conditional binding so the articles are only shown when the screen’s in a specific mode. The screen mode will be set when the hyperlinks are clicked. To aid readability, the similar HTML for the list of books is omitted.

<!-- The top level menus -->
<ul id="menu">
    <li><a data-bind="navigationLink: 'article'">Articles</a></li>
    <li><a data-bind="navigationLink: 'book'">Books</a></li>
</ul>

<div id="content">
    <div data-bind="if: mode() == 'articles'">
        <!-- The list of articles -->
        <ul data-bind="foreach: articles">
            <li>
                <span data-bind="text: title" ></span>
                <div>By <span data-bind="text: author"></span></div>
            </li>
        </ul>
    </div>
</div>

The accompanying View Model has properties for the screen mode and articles array. There are two functions that set the appropriate screen mode. The first one also populates the articles array from a demo list of articles returned by the getArticles function.

function ViewModel(){
    var self = this;

    // The screen mode and articles
    self.mode = ko.observable();
    self.articles = ko.observableArray();

    // Populate the screen mode and articles
    self.showArticles = function(){
        self.mode('articles');
        var articles = getArticles();
        self.articles(articles);
    };

    self.showBooks = function(){
        self.mode('books');
    };
}

To ensure the showArticles function is called whenever the first hyperlink is clicked, we must map this function to the State that represents the article list view. The Navigation router exposes a dialogs object which we can use to access the ‘article’ Dialog and, from there, gain access to the ‘list’ State. We can then map the function to the State by assigning the showArticles function to the State’s navigated property.

var articleDialog = Navigation.StateInfoConfig.dialogs.article;
var articleListState = articleDialog.states.list;

// Map the function to the State
articleListState.navigated = self.showArticles;

So that the showBooks function is called whenever the second hyperlink is clicked, a similar mapping is made between it and the books ‘list’ State.

We’ll place a call to Navigation.start() when the page loads. This simulates a hyperlink click for the browser URL to start us off on the list of articles. You can see the top level hyperlinks all styled up and working in the following CodePen. If Knockout isn’t to your taste, functionally identical Angular and React CodePens are also available.

Filtering The Articles

Let’s move on to the hyperlinks that filter the list of articles by category. They appear in both the article list and book list views and, on a narrow screen, sit directly below the top level menu.

Filtering the Articles

The only difference between these hyperlinks and the first top level menu hyperlink we’ve just built is that they need to pass along the category filter. We can augment the navigationLink custom binding with a toData object containing the category name. To keep the demo simple, we’ll only create the first two out of the set of six hyperlinks.

<ul id="categories">
    <li><a data-bind="navigationLink: 'article',
            toData: { category: 'coding' }">Coding</a></li>
    <li><a data-bind="navigationLink: 'article',
            toData: { category: 'design' }">Design</a></li>
</ul>

The corresponding View Model change is to use the category name to filter the list of articles. By adding a data parameter to the showArticles function, we can get our hands on the selected category.

self.showArticles = function(data){
    self.mode('articles');

    // Use the data.category to filter the articles
    var articles = getArticles().filter(function name(article) {
        if (!data.category)
            return true; 
        return article.category === data.category;
    });
    self.articles(articles);
};

Let’s pass the category name as a route parameter rather than in the query string. We’ll revisit the configuration of the State for the article list view and change the route to ‘{category?}’. The ‘?’ indicates the parameter’s optional and is needed because there’s no category passed when the top level menu hyperlink is clicked.

var articleDialog = {
    key: 'article', initial: 'list', states: [
        { key: 'list', route: '{category?}' }
    ]
};

You can take a look at the category filters in action in Angular or React or in the Knockout CodePen below.

Paging The Articles

A set of paging hyperlinks appear just below the list of articles. It’s their turn for the Navigation router treatment.

Paging the Articles

Clicking a paging hyperlink keeps us on the article list view, with only the page number changed. Because the view doesn’t change, there’s no need to specify the ‘article’ key when creating the paging hyperlinks. Instead, we can use the refreshLink custom binding which only requires the data to pass.

<a data-bind="refreshLink: { page: 2 }">2</a>

We mustn’t forget about the category filter when creating the paging hyperlinks. After filtering by ‘Coding’, for example, changing the page number must keep us within that category. We can rely on the Navigation router’s excellent memory to keep track of the chosen category for us. Telling the refreshLink binding to include the current data will ensure our paging hyperlinks include this filter.

<div id="paging" data-bind="foreach: pages">
    <a data-bind="refreshLink: { page: page }, 
        includeCurrentData: true, text: page"></a>
</div>

This HTML expects a pages array property on the View Model, containing an item for each available page.

self.pages = ko.observableArray();

Before we make the remaining changes to the View Model to accommodate the pagination section, we’ll make our lives easier by configuring a default value for the page number. Guaranteeing a value for the page number avoids those annoying existence checks that can quickly clutter up an otherwise tidy View Model.

var articleDialog = {
    key: 'article', initial: 'list', states: [
        { key: 'list', route: '{category?}', defaults: { page: 1 } }
    ]
};

We’ll change the showArticles function on the View Model to populate the array of pages. Smashing Magazine shows eight articles per page but, to keep the demo article list to a reasonable length, we’ll dial this down to three. The page number passed in is used to pick out the right slice of the articles array.

self.showArticles = function(data){
    self.mode('articles');
    var articles = getArticles().filter(function name(article) {
        if (!data.category)
            return true; 
        return article.category === data.category;
    });

    // Calculate the number of pages
    self.pages.removeAll();
    for(var i = 0; i < Math.ceil(articles.length / 3); i++){
        self.pages.push({ page: i + 1 });
    }

    // Use the data.page to pick out the articles to display
    var start = (data.page - 1) * 3;
    articles = articles.slice(start, start + 3);
    self.articles(articles);
};

You can play with the pagination and filters in the Knockout CodePen below, or in the corresponding CodePens for Angular and React.

Selecting An Article

The last hyperlink in our sights is the article title which, when clicked, takes us to the full text of the article. First, let’s create a State for this new view and add it to the ‘article’ Dialog that we already have. This Dialog will then group together the States representing our two article-centric views. Because the two views are connected together by the title hyperlink, we’ll connect up the two States using a Transition. The Transition, representing the direction of travel of the hyperlink, is added to the first State and points at the second.

var articleDialog = {
    key: 'article', initial: 'list', states: [
        { key: 'list', route: '{category?}', defaults: { page: 1 },
          transitions: [
            // The Transition from the article list to details
            { key: 'select', to: 'details' }
        ]},
        // The article details State
        { key: 'details', route: 'article/{slug}' }
    ]
};

We gave this new State a route of ‘article/{slug}’ because Smashing Magazine URLs contain slugs, or user-friendly keywords, to identify the article selected. We’ll use the ‘select’ key of the Transition that connects the States to create the title hyperlink, passing along the article’s slug in the toData.

<ul id="articles" data-bind="foreach: articles">
    <li>
        <a data-bind="navigationLink: 'select', 
            toData: { slug: slug }, text: title" ></a>
        <div>By <span data-bind="text: author"></span></div>
    </li>
</ul>

The HTML for the article view starts with a screen mode check, followed by bind statements that pull together the properties of interest from the selected article.

<div data-bind="if: mode() === 'article'">
    <div id="article" data-bind="with: article">
        <h2 data-bind="text: title"></h2>
        <div>By <span data-bind="text: author"></span></div>
        <p data-bind="text: text"></p> 
    </div>
</div>

The supporting View Model changes are the addition of an article property and a showArticle function. The function sets the screen mode, then populates the article using the slug passed in.

self.article = ko.observable();
self.showArticle = function(data){
    self.mode('article');

    // Use the data.slug to find the article
    var article = getArticles().filter(function name(article) {
        return article.slug === data.slug;
    })[0];
    self.article(article);
}

If we forget to map the showArticle function to the new State, the function won’t be called when the title hyperlink is clicked.

var articleDetailsState = articleDialog.states.details; 
articleDetailsState.navigated = self.showArticle;

It would be handy to have a hyperlink below the article that remembers our place in the list, so that, once we’ve finished reading, we can return to the exact category and page we were on before selecting the article. The Navigation router’s photographic memory let’s us create such time-travelling hyperlinks using the navigationBackLink custom binding.

<a data-bind="navigationBackLink: 1">Continue browsing</a>

The Navigation router achieves this feat of memory by storing data in the URL. So, for views that don’t need navigationBackLink bindings, it’s best to disable this feature by setting trackCrumbTrail to false against the view’s State representation. We can safely turn it off for both the article list and book list views.

var bookDialog = {
    key: 'book', initial: 'list', states: [
        { key: 'list', route: 'books', trackCrumbTrail: false }
    ]
};

Put the Navigation router’s memory to the test by clicking around in the Angular, React or Knockout CodePens.

Can Your Router Do That?

Thanks to the diligent and unassuming Navigation router, we’ve built a mini replica of the smashingmagazine.com website as a Single Page Application. In fact, we’ve built three mini replicas, one in each of Angular, Knockout and React. We’ve created all the hyperlinks using the UI syntax native to each library. The end result is a lean and mean code base, with no hyperlink construction code leaking into the Knockout View Model, for example.

We wouldn’t have fared nearly so well if we’d used a lazy or overbearing router. With a lazy router, we would’ve soon grown tired of the URL manipulation code required to build the variety of hyperlinks on show. An overbearing router might have started out fine but, because it has such a short memory when compared to the Navigation router, would’ve ended up struggling just as much as any lazy router. If you cherish hyperlinks and the freedom to create your UI however you wish, the Navigation router’s your only choice.

It’s Not Me, It’s You

Me: I don’t think we should see each other any more.
Codeplex: Why? What have I done?
Me: Nothing. That’s the problem.
Codeplex: I can change.
Me: You’ve been saying that for years.
Codeplex: I know, but this time I mean it.
Me: I’ve found someone new.
Codeplex: Do I know them?
Me: Yes.
Codeplex: Who is it?
Me: Github.
Codeplex: [sobs]

My Navigation library has moved to Github.

Get Your Paging Sorted

Why’s it so hard to build a paged list in ASP.NET MVC? Because paged lists typically need filtering and sorting and this simultaneous tracking of the current page number, filter criteria and sort order is no mean feat. If we’re not careful, we’ll lose the current sort order when clicking a paging Hyperlink or the filter criteria will be forgotten when clicking a sorting Hyperlink. The Navigation for ASP.NET project takes a new approach to managing data that makes it easy to build Hyperlinks that are amnesia-proof.

It’s In the Bag

With Navigation for ASP.NET we track our Url parameters in a data bag called NavigationData. At the start of each request this NavigationData is primed with all the Url’s Route and QueryString data. Clicking a paging Hyperlink, for example, would put the current page number into NavigationData.

When we build a Hyperlink we supply the new data and the Navigation project will combine this with the current NavigationData to create the Url. To build a sorting Hyperlink, we need only pass the new sort order and then the current page number held in NavigationData will be included. Paging Hyperlinks built in similar fashion will automatically remember the sort order.

Html.RefreshLink("Sort", 
	new NavigationData{{ "sortExpression", sortString }}, true)

But the filter criteria comes in as part of a posted HTML form and so isn’t loaded into NavigationData when the request begins. If it’s not part of the NavigationData then it won’t appear in our Hyperlinks and will be forgotten when paging or sorting. But unlike Route or QueryString data, NavigationData is writeable, allowing us to manually add in the filter criteria from our Controller code. Having made it into NavigationData, the filter criteria will be included in the paging and sorting Hyperlinks.

StateContext.Bag.filter = filterString;

Take a peek at my paging, sorting and filtering code sample to see how easy it is. If the Pager and Sorter method calls in the razor code look unfamiliar, rest assured that under the covers they both call to the RefreshLink extension method talked about here.

What’s All the Todo About?

The Navigation for ASP.NET project makes building Single Page Applications easy. Just divide up your Razor views into panels and the Navigation project will use changed panel content from the server to update the innerHTML of corresponding elements on the client. The self-appointed SPA police would have us believe that this isn’t a legitimate technique for building Single Page Applications, but my TodoMVC implementation says otherwise.
Creating a todo
Some parts of TodoMVC seem impossible to build using this technique. Take creating a todo as an example, where, once created, the new todo is appended to the list. How’s it possible to single out the new todo’s HTML on the server? And which client element will hold this new todo’s HTML so that it appears in the list?

Spot the Difference

We’ll start on the server by dividing up our Razor view so that each todo is inside its own panel.

<ul id="todo-list">
@foreach (var todo in Model.Todos) {
    <li>
        @Ajax.RefreshPanel("todo" + todo.Id,
            (context, fromData, toData) => true,
            @<div>
                <!-- Todo details -->
            </div>)
    </li>
}
</ul>

The RefreshPanel Extension method accepts a Func that indicates whether the content has changed. Because only changed content is sent to the client, if that Func only returned true for the newly created todo our job would be done. So the Func needs access to the newly created todo id and our Controller can provide this by storing it in the HttpContext.

(context, fromData, toData)  
	=> (int?) context.Items["todoId"] == todo.Id

Draw a Blank

There isn’t a corresponding element on the client to hold the HTML, but if we can create an empty element in the list with a matching id before the Navigation logic runs then all will be well. The Navigation project’s JavaScript Api exposes an updating event that fires early enough where we can extract the id from the response panel passed in and create the new list item.

refreshAjax.updating(function (req, resp) {
	var panelId = Object.keys(resp.panels)[0];
	var todoList = document.getElementById('todo-list');
	todoList.insertAdjacentHTML('beforeend', 
		'<li><span id="' + panelId + '" /></li>');
});

Todon’tMVC

TodoMVC is a site that helps you choose between JavaScript frameworks, letting you pick the one with the smallest code base or nicest data-bind syntax, for example. But it doesn’t tell you which performs the best. So which one deserves the title of the fastest TodoMVC? None of them. They’re all slow…very…slow. Keep reading to find out why they’re so sluggish and for a new TodoMVC implementation that leaves them all scrabbling in its wake.

At first glance they look fast, many capable of adding 100 todos in less than a second. But from a user’s perspective this is a pointless measure because it takes a good few seconds just to type in the name of one todo. A more important metric is how fast the site loads on first visit. There’s plenty of research indicating that this is where the milliseconds really count and can mean the difference between keeping or losing a user. And this is where these JavaScript frameworks uniformly suck.

Quickest on the Draw

Why is getting initial content to the user the Achilles’ heel of these JavaScript frameworks? It’s because they use client-rendering, where the HTML is created using JavaScript. Before any HTML is drawn to the screen a substantial JavaScript file must be downloaded, parsed and executed. This can add seconds to the first visit load time. But we’ve already got a fast way of getting initial content to the user. It’s server-rendering, where the HTML is created on the server and returned fully formed in the first request.

But won’t disbanding these JavaScript frameworks in favour of server-rendering mean that performance will suffer for all actions other than the first visit load? Not if we use the Navigation for ASP.NET project which makes building server-rendered Single Page Applications easy. After the first load it uses Ajax to retrieve only the portions of the page that have changed and uses the HTML5 History Api to keep browser history working.

Which is the fastest TodoMVC? Navigation TodoMVC is the fastest.

When you’re considering how to build your next Single Page Application have a look at how good the server-render story is for your JavaScript framework of choice and then take a look at Navigation for ASP.NET.