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.

Advertisements

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.

Action-packed Single Page Applications with ASP.NET MVC

In my last post we saw how easy it is to build Single Page Applications using the Navigation for ASP.NET project. The uniqueness of this approach is down to the ingenious way Hyperlinks are built. It means we can create a regular Razor page then flick a switch to turn it into an SPA.

Hyperlinks are all well and good but we also need the ability to update data. From a code-maintenance perspective we’d like to keep our Update Controller Actions separate from our Index Action. But we can’t do this by registering new routes because an SPA means a single page which in turn means a single route. Keep reading to see a couple of Attributes come to our rescue or stop reading to see a working example.

Go Where the Action is

Routes aren’t the only way to distinguish Actions. By creating an Attribute inheriting from the ActionNameSelectorAttribute we can override the IsValidName method to decide which Action to call based upon a request parameter. Then each submit button’s value signifies the Action to invoke.

public override bool IsValidName(
	ControllerContext controllerContext, 
	string actionName, 
	MethodInfo methodInfo)
{
	actionName = controllerContext.Controller
		.ValueProvider.GetValue("action").AttemptedValue;
	return actionName == methodInfo.Name;
}

When the Update Action completes we must return to Index.cshtml because that’s the Single Page in our SPA. Returning the View from our Update Action won’t do the job because we also need the ViewModel constructed by the Index Action. But our aim is to keep the Actions separate so we can’t just call out to the Index Action and, because we’re building an SPA, we can’t Redirect to it either. Have we come unstuck?

An Action in Action

Redirects aren’t the only way to move between Actions. The trick is to create a new Child Action and to treat this as the Index Action instead. So we’ll move the code from the Index Action into the Child Action and the Razor from Index.cshtml into Child.cshtml. Then we’ll change Index.cshtml to execute the Child Action.

Now when the Update Action completes we can return to Index.cshtml safe in the knowledge that our Child Action will take care of the rest. We’re not out of the woods yet though because any validation messages set by the Update Action will be lost. That’s because Child Actions don’t share their Parent’s ModelState, which is where validation messages are stored. We can put things right by decorating our ChildAction with an ActionFilter Attribute that syncs up the ModelState in its OnActionExecuting method.

public void OnActionExecuting(
	ActionExecutingContext filterContext)
{
	var parentModelState = filterContext.
		ParentActionViewContext.ViewData.ModelState;
	var modelState = filterContext.
		Controller.ViewData.ModelState;
	foreach (var item in parentModelState)
	{
		if (!modelState.ContainsKey(item.Key))
		{
			modelState.Add(item.Key, item.Value);
		}
	}
}

Link Yourself Rich with Navigation for ASP.NET

In my previous post I introduced a new way of building Single Page Applications for ASP.NET MVC. People asked ‘Is it just ASP.NET Ajax but for MVC?’ The answer’s a resounding ‘No’, but I can understand why they asked because both use Ajax to retrieve server rendered panels of HTML. Read on to see how my new approach embraces Hyperlinks in a way that makes it different not just from ASP.NET Ajax but from all of the other SPA-building frameworks clamouring for the attention of ASP.NET MVC.

A Refreshing Change

In ASP.NET MVC we build a Hyperlink from a route and data. To Navigation for ASP.NET, a Hyperlink returning to the same route is called a Refresh Link and we split its data into two kinds: current data and new data. The current data remembers how the page looks, before the new data comes along and changes it. We’ll revisit the Gmail navigation sample from my previous post to work out the current and new data required to open an email from the second page of the inbox.

Going to the second page

Going to the second page
To go to the second page we need the new start index. We also need the current folder, without which we’d forget we were browsing the inbox. We build the Hyperlink using the RefreshLink extension method passing the new and current data as the second and third parameters.

@Html.RefreshLink("Next", new NavigationData { { "start", 50 } }, "folder")

Opening an email

Opening an email
To open the email we need the new email id. We also need the current folder and start index so we can return to the correct page of our inbox when we close the email. This time, instead of passing the two pieces of current data, we’ll pass true as the third parameter to indicate we want to retain all the current data.

@Html.RefreshLink("Message", new NavigationData { { "id", 1 } }, true)

Get Rich Quick

By starting with the Hyperlinks we should end up with a fast, accessible page working on all devices and visible to all search engines. But how do we turn this page into a Rich Internet Application or SPA? It’s a simple matter of dividing our razor view into panels, indicating when they should be refreshed based on changes to the NavigationData, and letting Navigation for ASP.NET do the rest for us. Navigation for ASP.NET hijacks a Hyperlink’s click event and fetches the changed panels’ HTML over Ajax. It only does this in browsers supporting the HTML5 History API so that the back button and bookmarking still work.