Building SilkThread.pw using NancyFX

Background

Those who have been following my Knockout Component series of articles will know I am attempting to build a little SPA framework of my own here. No, it’s not the next best thing since sliced bread, just my attempts at learning Javascript, TypeScript and putting together a bunch of really cool libraries.

To try the entire thing online I decided to purchase a domain and the cheapest one was a .pw. SilkThread sounded like a nice name for a framework that was aiming to string together a series of well written libraries. So here we go, I am going to document my steps on creation of the first cut of http://SilkThread.pw, powered by Nancy and SilkThread itself Smile.

Who Nancy?

Well, it’s not a who, but a What. Nancy FX is an Open Source Web Framework in .Net. Conceptualized and initially built by Andreas Håkansson and supported by an awesome community of .NET devs, it’s now 3+ years old (here’s the list of the contributors). It is inspired by Sinatra in the Ruby world, but I don’t know enough about Sinatra to talk more about the history. It’s primary goal was to help build HTTP services in .NET. However, Nancy is more, it can serve up static content, html and even supports multiple view engines. So its like the best of both AspNET MVC and Web API worlds in one neat bundle.

Why NancyFX instead of AspNet MVC?

Well, no reasons really! Since my SPA effort is aimed at learning stuff and all of it is Open Source, I decided to try an Open Source framework that’s contemporary to the Asp.NET MVC!

My only requirement was that it should be host-able on IIS via my shared hosting provider. Once my Twitter pals Phillip Haydon and Filip Ekberg confirmed this is possible, I was all set to give it a go. I should also mention the gentle nudges from Phillip and Jonathan Channon to try out NancyFX contributed handsomely too Smile.

So no ‘political’ reasons, just an eager beaver trying out a new framework. You have already seen how to do everything I do here using ASP.NET MVC.

Getting Started

Nancy has some neat Visual Studio project templates, so you can get started directly using these templates. But that’s less of fun than learning things ground up. So I decided to start with an Empty Web template.

imageimage

On clicking Ok, a new empty project is created, however we’ll see it has got a bunch of dependencies that I don’t think are required.

image

So we remove the ones shown selected above.

Next we add two Nancy packages from Nuget. If you are using Package Manager console you can get them using the following commands:

PM> install-package Nancy

PM> install-package Nancy.Hosting.AspNet

These two packages are all you need to get started.

A quick “Hello Nancy”

The Nancy equivalent of a ‘’AspNET MVC/WebAPI Controller” is the NancyModule. I have created a Modules folder and created added a HomeModule class in it. The Modules folder will hold our controller layer. In the root, I’ve added a IndexPage.html

image

Next I’ve updated the HomeModule to inherit from Nancy.Module.

In the constructor I’ve added a root path and assigned a method that returns a ViewRenderer. We’ve passed the IndexPage.html to the ViewRenderer because we don’t want any sever side processing done and want to show the HTML as is.

public class HomeModule : Nancy.NancyModule
{
public HomeModule()
{
Get["/"] = GetIndex;
}

    private dynamic GetIndex(dynamic arg)
{
return View[@"/IndexPage.html"];
}
}

The IndexPage.Html simply says “Hello Nancy”.

<!DOCTYPE html>
<html xmlns=”
http://www.w3.org/1999/xhtml”>
<head>
<title></title>
</head>
<body>
Hello Nancy
</body>
</html>

With that set up, hit F5 and you should have a vanilla page on your default Browser as follows:

image

Well then, the myth of OSS projects being complex to get started with, stands BUSTED! Yes, Nancy is truly a low ceremony, awesome web framework.

But this is not what we want to setup do we? We want a nice Bootstrap Styled SPA! Well, let’s get to work.

Getting SilkThread into the project

To get SilkThread into the project, the best way would have been to get it via Nuget. But creating a Nuget package is a separate post, so we’ll use the age old technique of ‘copy-paste’. I have the latest code from the main branch of the project from Github. So I copied the App and Scripts folder into the project and re-arranged them a little. My new project structure is as follows:

image

I have made the following changes

  1. Created a Views folder and moved the Index.html to it.
  2. Created a Scripts/libs folder and moved all folders previous under Scripts to Scripts/libs
  3. Created a Scripts/app folder and moved the contents of the previous /App to Scripts/app
  4. The above folder realignment requires us to modify the Scripts/app/boot/require.config.js and change it as followsvar require = {
    baseUrl: “/
    Scripts/“,
    paths: {
    “bootstrap”: “libs/bootstrap/bootstrap”,
    “historyjs”: “libs/history/native.history”,
    “crossroads”: “libs/crossroads/crossroads”,
    “jquery”: “libs/jquery/jquery-1.9.0″,
    “knockout”: “libs/knockout/knockout-3.2.0beta.debug”,
    “knockout-projections”: “libs/knockout/knockout-projections.min”,
    “signals”: “libs/crossroads/signals”,
    //”hasher”: “Scripts/crossroads/hasher”,
    “text”: “libs/require/text”
    },
    shim: {
    “bootstrap”: {
    deps: ["jquery"]
    }
    }
    }
  5. Next, install the bootstrap nuget package and removed the scripts folder as we already have the scripts in the libs folder.
  6. Finally we update the GetIndex method in the HomeModule to point to the Views/Index.htmlprivate dynamic GetIndex(dynamic arg)
    {
    return View[@"
    Views/Index.html"];
    }

Now if we run the project we’ll get the following:

super-500-page-by-nancy

The ‘Oatmeal’ has greeted us! Nancy tries hard to tell us what happened and it seems it looked at lots of places but couldn’t find a file. Fear not, it’s a small detail but it’s a safe default.

Explicitly specifying content folders

Basically Nancy serves static content off the Content folder only by default (html files are served from any folder as long as you’ve got the routing right). So when our Index page is trying to access the Scripts folder, things are falling over. To use a custom folder structure like we defined above there are two options a. We move Scripts folder under Content and roll with it OR b. we tell Nancy about our folder structure. Let’s try the second option, it’s rather easy to do so.

  1. Add a new class called SilkThreadBootstrapper and inherit if from Nancy.DefaultNancyBootstrapper
  2. Override the method ConfigureConventions method and use the StaticContentConventionBuilder helper to add folders that you want Nancy to track.
  3. The full code is as follows, as you can see I’ve added the Scripts folder and the Views folderpublic class SilkThreadBootstrapper : Nancy.DefaultNancyBootstrapper
    {
    protected override void ConfigureConventions(
    Nancy.Conventions.NancyConventions nancyConventions)
    {
    base.ConfigureConventions(nancyConventions);

            nancyConventions.StaticContentsConventions.Add(
    Nancy.Conventions.StaticContentConventionBuilder.AddDirectory(“Scripts”));

        }
    }

With those changes done, if we fire up our app, it will come up smoothly.

image

Sweet!

Deploying to the hosting Site

My host doesn’t support a fancy “Deploy from Github on commit to master’’ functionality. So I did it the amateur way:

  1. Got the IIS Web Publishing settings from my host
  2. Imported them into the project.
    Tear hair out because Web Publish keeps giving a weird error. Either provider or Visual Studio is broken.
  3. Enable ftp upload
  4. Setup ftp Deploy and Publish

Here you go! http://silkthread.pw

image

(Note: I will be updating the site to be a little more useful so expect it to change over time).

If you look at the deployment folder it’s got only three dlls, with the total deployment size < 1MB. That’s pretty cool!

image

Conclusion

Nancy FX is a neat little framework with a tiny footprint for quick dash web projects. However, I have not even skimmed the surface of it’s abilities. To try out more I’ll continue to build SilkThread’s site using Nancy. So there will now be two streams of articles in parallel! One, features in SilkThread and the other about updates to the Site using Nancy.

Special Thanks to Phillip Haydon, for proofing the Nancy bits and pointing out some gotchas I comitted as a nOOb.

Source Code

The source code for SilkThread.pw is going to reside at Github, I’ll try to branch it as I go!

https://github.com/sumitkm/silkthreadspa

Tagged , , , ,

Part 5: SPAs, Accessibility and the Server side

Part 1: Dipping your feet into KnockoutJS Components
Part 2: Knockout Components – Separating Templates from View Model
Part 3: KO Components – Routes and Hashes
Part 4: SPA with KO Components-PushState and History JS

Recap

In the previous 4 parts we have seen how to use Knockout Components to make a sample Single Page Application, with different type of client routing libraries.

Towards the end of the third article I had highlighted how SPAs hinder Accessibility and Search-ability of a site’s content primarily because:

a. Search engines don’t ‘click’ on URLs to load content, they simply follow URLs they find and request the server for content. Whatever the server returns is indexed as content of that URL.

b. Accessibility tools work on similar logic to Search engines and cannot ‘click’ on URLs to load content.

Towards both these ends we took a small first step by moving from a hash based client side router to a pushState based one that falls back gracefully for non-push state browsers. This gave us URLs that looked like /settings instead of #settings. The search engine or accessibility tool now knows it has to navigate to a new URL to get the content.

At the end of the last article I has shown how the server would return a 404 or some similar server-side error because when we click on http://buildingspausingko.azurewebsites.net/settings there is no page at that URL. Remember our entire site is being served from index.html. How to we fix this?

Enter server side routing fallbacks

Before we start let’s get the Part 4 code from Github.

Using _Layout and adding an Index.cshtml for the home page

Before anything, let’s start using the server in the first place.

Our Application was an Empty ASP.NET application to start with. So to get started we’ll first add ASP.NET MVC to it. We can do this by installing the package Microsoft.AspNet.Mvc

PM> install-package Microsoft.AspNet.Mvc

Now let’s add Controller/HomeController and a Views/Home/Index.cshtml files.

We update the Layout such that it contains the top navigation bar and then calls the RenderBody function to render the rest of the page.

<!DOCTYPE html>
<html xmlns="
http://www.w3.org/1999/xhtml">
<head>
    <title>Dipping your feet into KnockoutJS Components</title>
    <link href="/Content/bootstrap.css" rel="stylesheet" />
    <link href="/Content/bootstrap-theme.css" rel="stylesheet" />
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/">KO Components</a>
                <ul class="nav navbar-nav">
                    <li>
                        <a href="/">Home</a>
                    </li>
                    <li>
                        <a href="settings">Settings</a>
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <div id="page1" class="container" style="padding-top:50px">
        @RenderBody()
    </div>
    <footer class="navbar navbar-fixed-bottom">
        <div class="container-fluid">
            <p> &copy; 2014 – Still Learning</p>
        </div>
    </footer>
    <script src="App/boot/require.config.js"></script>
    <script data-main="App/boot/startup" src="Scripts/require/require.js"></script>
</body>
</html>

We put in a simple header in the index.cshtml file showing that it is coming from the server. However not that the div containing the Index content is also the container for the dynamic component which will be loaded by Require JS.

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="page1" class="container" style="padding-top:50px;" data-bind="component: { name: route().page, params: route }">
    <div class="container" style="background-color: red">
        <h1>Index from Server</h1>
    </div>
</div>

Finally, let’s rename the Index.html to Index2.html so that it’s not picked up as the default by IIS.

If you run the application now you’ll see the ‘Index from Server’ text flashes briefly before being replaced by the greetings.

This simply implies that there was a page that the server rendered and returned, once the page was loaded the client side routing kicked in and replaced the container.

This was the default page.

In the browser change URL by adding settings you’ll get an error as follows:

image

This is because there is an invalid redirection in my web.config. Open the web.config and delete the highlighted section

image

Now if you type in the URL /settings on the browser you’ll get a proper 404

image

You must be wondering how come when I click on Settings link from Home page I get a proper page, but when I type in the URL I get a 404? Well, that’s because typing in a URL sends the request to the server directly. In our case, it sends to ASP.NET MVC. MVC framework uses the current route info and tries to map it to a ‘SettingsController’ with a default ‘Index’ action method. However it doesn’t find the SettingsController and throws a 404.

This is what will happen to Search Engines and Screen readers as well. Even though they will see a URL, trying to browse to it will get them a 404. Search engines will down-rate your site and screen readers will give a poor experience to people using them.

So how do we solve this? Let’s explore two ways today.

Sending the same page back for all URLs from Server

This is a hackish technique to make sure your users can navigate to the correct page from bookmarked URLs. This doesn’t really help in Search engine optimization or screen readers. But atleast no one gets a 404 when a valid URL is accessed directly from the browser.

For this, all we have to do is, manipulate the MVC router to send all requests to HomeController’s Index action method.

Once the Index page is returned the client side router will kick in and do the ‘right thing’.

In the App_Start\RouteConfig.cs update the RegisterRoutes method as follows:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
   
routes.MapRoute(
                name: "Default",
               
url: "{*pathinfo}",
                defaults: new { Controller="Home", action = "Index", id = UrlParameter.Optional }
            );
}

As you can see above, we have changed the url parameter to be a wildcard {*pathinfo}, and set the default for this wildcard path to Home Controllers’ Index action method.

If you run the application now, and navigate to the Settings page by typing in the URL you’ll see that there is no more 404 error. BUT, the client side routing seems to be taking us back to the Index page. Why?

image

Removing hard coded default path in router.js

Well, in our ‘excitement’ to get History.js going with crossroads.js in the last article, we introduced a small hardcoding that’s causing the about “BUG”.

In Router.js’ activateCrossRoads function the last line instructs crossroads to route to the root location (‘/’).

crossroads.parse(‘/’);

This is why even though the browser is pointing to settings page, the page is showing the greetings from the Index page.

Let’s fix this.

The inline function call that’s being bound to the stateChanged event actually needs to be called on first Initialization as well. So let’s refactor it out to a separate function called routeCrossroads.

function routeCrossRoads()
{
    var State = History.getState();
   
if (State.data.urlPath) {
        return crossroads.parse(State.data.urlPath);
    }
    else {
        if (State.hash.length > 1) {
            var fullHash = State.hash;
            var quesPos = fullHash.indexOf(‘?’);
            if (quesPos > 0) {
                var hashPath = fullHash.slice(0, quesPos);
                return crossroads.parse(hashPath);
            }
            else {
                return crossroads.parse(fullHash);
            }
        }
        else {
            return crossroads.parse(‘/’);
        }
    }
}

I have made some changes to the code in the else section. Actually the condition to check for ‘?’ is there to handle IE9 properly. In case of IE9, History appends a state Id to the URL after ‘?’. Crossroads doesn’t need the state ID hence I strip it out. But the initial condition is incomplete because if there are no ? in the URL we need the entire path (fullHash). I have fixed this in the above code.

Once the above function is in place, the activateCrossroads function becomes as follows:

function activateCrossroads() {
    History.Adapter.bind(window, "statechange",
routeCrossRoads);
    crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
    //crossroads.parse(‘/’);
    routeCrossRoads();
}

As we can see we have commented out the default routing to the root ‘/’.

With everything in place if we run the app now, and try to type if /settings at the end of the URL we’ll get the correct settings page.

image

But again, before the Settings page is loaded you will briefly see the red banner saying ‘Index from Server’ flash briefly before navigating to the ‘’Settings’ page. This is because for all URLs we are returning the same Index page from the Server. How do we fix this, if the answer wasn’t obvious, read on Smile

Technique 2: Sending a Server Side page for every URL

Now that our SPA is capable of responding to all URLs specified in our web app, let’s dig deeper to see how we can respond to all pages equally.

This section is experimental on my part, if there are better ways to do this, please sound off in the comments section.

Updating the MVC routing

First thing to do is to update the Routetable to add independent routes for Home/Index and (say) Settings/Index pages.

We’ll keep the ‘catch all’ route that we defined earlier so that if there are any routes that are not defined on the server but defined on the client, we send them off to the home page to load the old fashioned way.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        name: "Default",
        url: "{Controller}/{action}/{id}",
        defaults: new { action = "Index", id = UrlParameter.Optional }
    );

    routes.MapRoute(
        name: "
CatchAll",
        url: "{*pathinfo}",
        defaults: new { Controller="Home", action = "Index", id = UrlParameter.Optional }
    );
}

With the above routing in place, lets add a new controller called SettingsController and a new cshtml at Views/Settings/Index.cshtml

The Settings Index file again is a simple one saying it’s coming from server! It uses the _Layout.cshtml file for the header.

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="page1" class="container" style="padding-top:50px" data-bind="component: { name: route().page, params: route }">
    <div class="container">
        <h1>Settings from Server</h1>
    </div>
</div>

Now when we run the application and type in /settings we’ll get the server page first and then the client page will get loaded, so you’ll see a brief flash saying ‘Settings from Server’.

In real world the duplication of cshtml and html templates may be a significant effort, I am looking at making things easier in that front and will blog about it if I find anything better. As of now, if you want the best user experience for all types of users (as well as the search engine), this is an extra bit of work that you have to do. It’s not as bad as it sounds either, all you have to do is use the same service that you call during the HTTP GET operation from client, and bind that data to the CSHTML. You can skip elaborate styling and limit it to having correct markup laid out in an orderly fashion.

Code

As usual the code for this article is up on Github. I have branched this code off as Part5: https://github.com/sumitkm/BuildingSpaUsingKO/tree/Part5

Conclusion

With that we conclude this part of the series where we saw how we can handle bookmarked URLs on the server and client. This goes a long way in helping users who use accessibility tools use your site effectively.

More fine tuning will be required on the client side router to handle all scenarios but that’s for another day.

Next we’ll see how to handle URL parameters and more involved routing like Tabs.

Tagged , , , ,

Part 4: SPA with KO Components-PushState and History JS

In the previous three parts we have seen:

At the end of the third article we saw some problems with hash based routing primarily in terms of accessibility and search engine indexing. Also the fact that hashed URLs are semantically incorrect gives us enough reason to look for alternatives.

Today we’ll explore how we can overcome some of these problems and discover some new ones Winking smile.

HTML5 and manipulating browser history

HTML5 pushState usually refers to a set of APIs that HTML5 browsers can support, to give more control to the developers on the Browser URL and the Browser’s back button behavior. These include pushState, replaceState and the event popState. Here is a nice primer on pushState on developer.mozilla.org

Essentially, when you want to add a URL to the history you push the URL into browser history using pushState. This is done for free when you click on a link (or type in the URL) and a server returns the page. However, if you wanted to build an AJAX based navigation system where you want to intercept all link clicks and show content dynamically loaded via AJAX, the browser doesn’t know the URL’s correlation to the content. So you have to tell it explicitly what the URL is (e.g. when we are showing the ‘Settings’ page, the URL should be /settings). HTML 5 pushState and related APIs make this easier for us.

Ajax based apps also have had trouble with the browser back button. As users we are trained to hit the big honking ‘Back’ button on the browser when we want to go back to the previous page. This is correct expectation and our Web Apps should respect that. Doing AJAX based page/component loading can break this because you may have content loaded based on user interaction that is not recoverable when user hits the back button.

So ideally we should use the new found ‘pushState’ functionality to indicate on the URL the exact location of the current content that is being shown. Thus when a user hits the back button we can use the URL to load up the previous content again. Back button in HTML5 browsers fire a popState event that can be handled for these purposes.

Doing all this manually is a bit of boilerplate. In Part 3 we saw how crossroads+hasher does it (Back button is fully supported for # URLs. Today we’ll see how we can use another library called History.js. Reason for exploring History.js is that it supports the new pushState API and falls back on Hash-based routing for browsers that don’t support pushState, (one such browser, is IE9 and when I started, one of my ‘undeclared’ goals was to support IE9 and up).

Replacing Hasher.js with History.js in our sample

You can get the last version of my sample from the Part 3 branch on Github.

You can clone the History.js repo from Github here.

Your cloned folder should look like this:

image

The Scripts folder has the following the sub-folders. Mechanics behind these folder is actually pretty neat.

image

History JS Components and Dependencies

History JS depends on availability of the JSON object so for older browsers that don’t have JSON parsing built in, it refers to JSON2.js. It bootstraps itself differently based on what library you are using. So there are various adapters that bootstraps History JS correctly with the respective library.

image

The bundled* folder contains one file per-adapter. Each file contains JSON2.js, the respective Adapter and the entire HistoryJS library. So all you have to do is pick the file from either the bundled-uncompressed\ folder or if you want to use the minified version use the same file from the bundled\ folder. Note there are two subfolders under bundled\ folder – html4+html5 and html5. If you don’t want to support HTML4 the library is trimmed a little further, but we want it, so we’ll dive into the html4+html5 folder.

If you want to experiment with adapters you can pick the adapter and the History.JS file from the compressed or uncompressed folders.

In my sample I’ve picked the native.history.js file from the bundled-uncompressed\html4+html5 folder.

Adding History JS and loading via RequireJS

  1. Since it’s a base library we’ll put it in it’s own folder under Scripts

image

2. Next we update the App\boot\require.config.js to load HistoryJS after bootstrap is loaded and remove the Hasher.js dependency.

image

3. Modifying our Router: We will need multiple changes in our Router.js file to configure it to use History.js instead of Hasher.js.

a. We start by adding historyjs dependency in the module definition

image

b. Handling anchor clicks to prevent server requests directly: This is actually a big deal. Typically we navigate to a different page by specifying an Anchor tag that has a href pointing to the new page. It may be a full path or partial path. When user clicks on an anchor the request is sent to the server by the browser. When using Hasher we worked around this by using href’s that started with a #. This told the browser that the reference was in the current page itself and then we hooked into the event and did our AJAX magic to load a new page. This also had the side effect of putting a # in the URL irrespective of the browser.

To avoid # in the hrefs, we need to handle all href clicks globally and push the path to HistoryJS for managing and raising statechange events.

To do this we assign a click handler for all URLs inside the HTML body tag. We check if the href attribute starts with a #, it means it’s a deliberate reference to some place in the current page and we let the event pass through. If the href doesn’t start with a # we push the path to History using the pushState function. Note we are not using window.pushstate because we want History to manage the pushState for older browser that don’t have a native implementation.

image

c. Next we update the activateCrossroads() function to intercept HistoryJS’ statechange event and pass the new path to crossroads:

function activateCrossroads() {
   
History = window.History; // # 1
    History.Adapter.bind(window, “statechange”, function () { // # 2
        var State = History.getState();
        if (State.data.urlPath) { // # 3
            return crossroads.parse(State.data.urlPath);
        }
        else
        {
           
if (State.hash.length > 1) { // # 4
                var fullHash = State.hash;
                var hashPath = fullHash.slice(0, fullHash.indexOf(‘?’));
                return crossroads.parse(hashPath);
            }
        }
    });
    crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
    crossroads.parse(‘/’); // #5
}

#1: Gets instance of the HistoryJS. Next we bind a event handler for the “statechange” event.
#2: The event handler is fired every time a new URL is pushed using pushState. In a browser this happens automatically when we click on an anchor tag. But clicking on an anchor tag that doesn’t start with a hash # results in a get from the server. We want to control this
#3: If the State.data has a valid urlPath we extract the urlPath from state provided by HistoryJS and pass it to crossroads to navigate to. However if users bookmark the path, then the urlPath comes in null. The next bit of code handles this
#4: If the urlPath is undefined, then we check if the State’s hash length is > 1. This is an indicator of a bookmarked URL and we slice out the path that crossroads needs and let crossroads navigate to it.
#5: This is invoked on activation and by default our router navigates to the home page.

Testing it out

Well, we have made all changes we need so it’s a good time to test it out. I have deployed it to Azure websites via Github deploy, so as soon as I commit to the master branch it updates the http://buildingspausingko.azurewebsites.net/ (which unfortunately is down due to unscheduled Azure maintenance at the moment, so instead of my VM accessing the site, we’ll ‘fake it’ using IE11 DOM manipulation).

On IE11 the site navigates cleanly

imageimage

I emulate IE9 DOM on IE11 and I get the rather ugly hash based URL.

image

Well, old browser users have to live with some ugly stuff unfortunately. But here is the good part, if users bookmark that URL, History JS will do it’s best to return users to the same URL.

Code for this release is at https://github.com/sumitkm/BuildingSpaUsingKO/tree/Part4

Conclusion

So we’ve now replaced the Hash based routing of Hasher.js with the pushState based one by HistoryJS. We have also seen how HistoryJS gracefully falls back when using older browsers.

With that I’ll end this article here and push the server side manipulation to the next part.

Just so you know, on modern browsers that support pushstate currently if we visit the /settings page directly it will show us an invalid URL.

image

Ideally it will show a 404, but my 404 redirection is broken so it’s showing this weird error. Eitherways, we’ll end up with an error. In the next part we’ll see how we can fix this.

Tagged , , , ,

Part 3: KO Components – Routes and Hashes

Part 1: Dipping your feet into Knockout JS Components
Part 2: Knockout Components – Separating Templates from View Model

As we continue our journey to learn more about KO Components, today we look at how we can  navigate around different ‘views’ and manage hyperlinks.

There are multiple libraries out there that allow you to do this. Today we’ll see how to use Crossroads for routing and Hasher for managing URLs. They internally use the Signals JS library. All three are written by Miller Medeiros. Now you could do all things these three libraries to with Sammy.js too. I will probably look at Sammy.js at some point in future, but Sammy is more opinionated on how it does routing and event handling, while I wanted to go one level lower and fiddle around myself. BTW I am in no way promoting one over the other, just that today’s sample uses Crossroads, Hasher and Signals.

Adding dependencies

All three libraries are not available as Nuget packages so we’ve to get them from their Github Repos directly. I have linked to the repos above.

Apart from the above three we will also use knockout-projections extension library by Steve Sanderson. This provides helper methods to deal with KO arrays.

I have downloaded them and added to the solution resulting in the following folder layout:

image
Figure 1

Updating require.config.js

We update the require.config.js to add the additional references so Require knows where to get them from as and when required.

var require = {
    baseUrl: “/”,
    paths: {
        “bootstrap”: “Scripts/bootstrap/bootstrap”,
       
“crossroads”: “Scripts/crossroads/crossroads”,
        “jquery”: “Scripts/jquery/jquery-1.9.0″,
        “knockout”: “Scripts/knockout/knockout-3.2.0beta.debug”,
        “knockout-projections”: “Scripts/knockout/knockout-projections.min”,
        “signals”: “Scripts/crossroads/signals”,
        “hasher”: “Scripts/crossroads/hasher”,
        “text”: “Scripts/require/text”
    },
    shim: {
        “bootstrap”: {
            deps: ["jquery"]
        }
    }
}

Restructuring the App layout and adding a page

Since we are going to demo navigation, it’s good to have at least one more page to navigate to. So we’ll introduce a loosely coupled concept of ‘page-components’. These are basically registered as ko.components but they are containers and in turn use other ko components.

As I said in my previous article the folder structure I was using is pretty arbitrary and open to updates.

Under App we will add a pages folder which will have all our ‘page’ components. I have added two, home and settings.

image
Figure 2

The home page-component

The Home page component has the standard HTML + JS structure. I have in-fact taken out the greeter instances from Index.html. We’ll see how we can pass data between two components. We’ll add a collection of ‘guests’ in home view model which will be passed to the greeter component. So our home.js will be as follows:

define(["knockout", "text!./home.html"], function (ko, homeTemplate) {
    function homeViewModel(params) {
        var self = this;
        self.title = ko.observable(‘Dipping your feet into KnockoutJS’);
        self.guests = ko.observableArray([]);
        self.guests.push({ guestName: “Sumit” });
        self.guests.push({ guestName: “Optimus” });
        self.guests.push({ guestName: “Bumblebee” });
        return self;
    }
    return { viewModel: homeViewModel, template: homeTemplate };
});

We have put the header text in a property called title. We also have an observable array of guests who the greeter component needs to greet. Instead of hard-coding the names this could have come from a server API call.

The home.html is as follows

<h2 data-bind=”text: title”></h2>
<hr />
<!– ko foreach: guests –>
<greeter params=’name: guestName’></greeter>
<!– /ko –>
<pre data-bind=”text: ko.toJSON($data, null, 5)”></pre>

We have bound the title to the header element.

Next we have a standard ko foreach that loops through each element in guests collection. The params attribute takes a JSON snippet so we pass it appropriately as name: guestName. KO can evaluate the observable and pass the appropriate value to the greeter component.

Setting up Routing using Crossroads and Hasher

In Figure 2 above you’ll note we have a new file called router.js.

The Router is defined as an AMD module. It takes in an object with the property routes that has an array of routes that are ‘registered’. So the routes property is going to hold all our routes.

As of now we have defined two routes, one is the home page that is at the root of the site and the second is the settings page that should be at the /settings URL.

When the module is initialized the Router function is invoked (acts as the ‘constructor’) with the routes defined. These are added to crossroads. I have added a method to log every routing event to the console.

The activateCrossroads function sets up Hasher to listen for hash changes and on change invoke crossroads to do the routing.

/// <reference path=”/Scripts/crossroads/crossroads.js” />
define(["jquery","knockout", "crossroads", "hasher"], function ($, ko, crossroads, hasher) {

    return new Router({
        routes: [
            { url: '', params: { page: 'home' } },
            { url: 'settings', params: { page: 'settings' } }
        ]
    });

    function Router(config) {
        var currentRoute = this.currentRoute = ko.observable({});

        ko.utils.arrayForEach(config.routes, function (route)
        {
            crossroads.addRoute(route.url, function (requestParams)
            {
                currentRoute(ko.utils.extend(requestParams, route.params));
            });
        });
        crossroads.routed.add(console.log, console);
        activateCrossroads();
    }

    function activateCrossroads() {
        function parseHash(newHash, oldHash){
            crossroads.parse(newHash);
        }
        crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;

        hasher.initialized.add(parseHash);
        hasher.changed.add(parseHash);
        hasher.init();
    }
});

With routing all set, let’s setup the Index page and register the new components before we can test out our changes.

Updating the Index.html page

The Index page has now become a nearly empty shell. We will add two links in the header, one for Home and the other for Settings page.

<div class=”navbar navbar-inverse navbar-fixed-top”>
    <div class=”container”>
        <div class=”navbar-header”>
            <button type=”button” class=”navbar-toggle” data-toggle=”collapse” data-target=”.navbar-collapse”>
                <span class=”icon-bar”></span>
                <span class=”icon-bar”></span>
                <span class=”icon-bar”></span>
            </button>
            <a class=”navbar-brand” href=”/”>KO Components</a>
            <ul class=”nav navbar-nav”>
                <li>
                   
<a href=”#”>Home</a>
                </li>
                <li>
                    <a href=”#settings”>Settings</a>
                </li>
            </ul>
        </div>
    </div>
</div>
<div id=”page” class=”container” style=”padding-top:50px” data-bind=”component: { name: route().page, params: route }”></div>


By default the page will land at the Home page and when user clicks on Settings the routing library will take it to the Settings page (or back to home page depending on the link clicked).

The div with id=page is our equivalent of RenderBody in MVC. The current page as defined by the route with be displayed here. As you can see it is using a div to bind any component that is presented to it by the router. If you remember we have setup two routes with page names home and settings. So these are the names of our home and page components. Let’s register them in startup.js.

Registering new components

We’ll add the two new components to startup.js and also initialize our router and pass it to ko as root view model.

To initialize router we use Require’s module loading to request for it in the required collection. Note the syntax ./router. This is because router.js is not a part of the Scripts folder rather it is in the same folder as the startup.js and also the fact that it’s not defined in require.config.js.

Another difference to note is how we are setting up settings component. This is the way to setup HTML only components that have no Javascript associated with them.

The home component is registered like greeter as we had done before.

define(['jquery', 'knockout', './router', 'bootstrap', 'knockout-projections'], function ($, ko, router) {
    ko.components.register(‘greeter’, { require: ‘App/components/greeter/greeting’ });
   
ko.components.register(‘home’, { require: ‘App/pages/home/home’ });
    ko.components.register(‘settings’, {
        template: { require: ‘text!App/pages/settings/settings.html’ }
    });
    ko.applyBindings({ route: router.currentRoute });
});

With our components registered, we are all set to try it out.

Demo time

When we run the app, and see the Debug window, you will notice we have downloads the styles and templates that pertain to the Index page, the Home page and the greeter component. The Settings component has not been downloaded yet.

image

Now if you clear the Network activity window and click on Settings, you will note that only one file is downloaded and that is settings.html. This may seem insignificant for this demo, but has a huge impact when your applications grow big and have hundreds of components (yes, they can have 100s of components, just take my word for it Winking smile).

image

It is all the more fun to note that now if you switch back to Home page no components are called from the server because the browser has everything it needs, so unless you want to force refresh the page with data from server, navigating between pages is as fluid as a client app!

image

So we have navigation between pages and just in time loads. What about the dreaded browser Back button? Well, if you play around with it, you’ll notice back button works perfectly fine and crossroads is able to switch between views thanks to Hasher monitoring hash changes. Excellent, we could have stopped here and gone home happy but…!

Hashes in URLs, bot accessibility and best practices

If you were a observing keenly, you would have noticed that when we navigated to settings page the URL is /settings instead it is #settings. This is because we are using the age old # prefix to prevent the browser from hitting the server when someone clicks on a link. Now you realize what Hasher is doing. It’s listening to the hashchanged event and invoking crossroads (albeit in a cross browser compliant way).

This gives us a problem where our URLs are not semantic. This is a headache from bot accessibility because search robots don’t really ‘invoke’ AJAX calls to get new views when they encounter a # url. There is a nice series by Derick Bailey on drawbacks of hash URLs on Los Techies site.

As you will see from his articles, HTML5 pushState comes to the rescue to a large extent. I say large extent because once we get pushState into picture we have to start looking at the server side as well because if you don’t have # in your URL, bookmarked URLs will directly request the server to serve the URL e.g. the /settings page. In our case there is no /settings page only index.html that uses client side manipulation to get you to /settings URL. But fear not, we’ll work everything out.

Pushstate is important enough to warrant a separate article in itself. You can refer to Derick’s article for the basics, his sample is in Backbone.js and Rails. You should be able to grasp the idea, but either ways I will do it in KO and ASP.NET MVC.

Side note – Google search and Hashbangs ( #! )

Once AJAX apps became popular Google came up with a suggestion for appending a bang ( ! ) after the hash to make it apparent to search engines that the URL warranted an AJAX call, but after the initial rush to hashbang everything, the industry settled back into semantic URLs primarily because of problems associated with handling #! at the server and it’s non-standard nature.

Source Code

I have branched the master out part Part3 and it is available on Github at https://github.com/sumitkm/BuildingSpaUsingKO/tree/Part3

Tagged , , ,

Knockout Components – Separating Templates from View Model

Part 1: Dipping your feet into Knockout JS Components

This is the second part in my Knockout JS Components series. So far we have made a simple ‘greeter’ component and used it multiple times. But our HTML template was rather simple and hard coded in the ViewModel as a string. That was fine for a small snippet but for involved templates you don’t want to stick an HTML as string. Rather you would like it to be an independent HTML that’s loaded as required.

Today we’ll see how we can separate the HTML and load it dynamically using another library called RequireJS and a plugin for it (RequireJS-Text).

If you want to follow along, you can get the source code branch for the previous article.

Quick intro to RequireJS

RequireJS is a library that is used for ‘Automatic Module Detection’ and loading of JavaScript modules and references. It was developed by James Burke from Mozilla. It is an open source project hosted on Github with a vibrant community that helps maintain it. It also has good documentation that you can refer to for getting started.

This article is not a RequireJS tutorial, instead we’ll jump right in and start using it, I’ll explain the syntax as we go along. If you have not used RequireJS before, well don’t panic, neither have I Smile.

Installing RequireJS and RequireJS-Text plugins

RequireJS has an official Nuget channel so getting the base library is easy to install via Nuget Package Manager Console, simply type in

install-package requirejs

Next we have to get the Text plugin from Github directly. You can either clone the repo from here https://github.com/requirejs/text or just download the JS file.

Once you have the file, add it to your scripts folder. I have started creating sub-folders for each library because it will come in handy later.

image

  • App/boot : This folder will contain scripts that initialize our libraries
  • App/components : This folder will contain all the components we create. Each component in turn has it’s own folder that may contain the ViewModel, HTML template and more.
  • Scripts/*: As mentioned earlier I’ve moved each library into it’s respective sub-folder under the Scripts folder. So Scripts is essentially for all libraries and frameworks that we will use (and not modify), and everything that we build will go under App.

NOTE: This folder structure is completely arbitrary. I ‘feel’ this works, you can use it, you can totally use your own structure. Just remember where your ‘root’ (folder) is Smile.

Now that we are done with the structure of the libs and sources let’s move on and configure RequireJS.

Configuring RequireJS

Under App/boot folder add a new JS file called require.configure.js. The name is, again, not a part of any convention, use what works for you, just refer it in Index.html correctly).

I have configured RequireJS initially as follows

var require = {
    baseUrl: “/”,
    paths: {
        “bootstrap”: “Scripts/bootstrap/bootstrap”,
        “jquery”: “Scripts/jquery/jquery-1.9.0″,
        “knockout”: “Scripts/knockout/knockout-3.2.0beta.debug”,
        “text”: “Scripts/require/text”
    },
    shim: {
        “bootstrap”: {
            deps: ["jquery"]
        }
    }
}

  • I’ve created a require global variable that has three properties, baseUrl, paths and shim.
  • The baseUrl property sets the root location with respect to which all other paths will be considered.
  • The paths property is assigned an object with name value pairs corresponding to the library names and their relative locations. Not the .js is stripped away from the paths.
  • The shim property provides additional dependency information. Here it shows bootstrap is dependent of jquery. The value ‘jquery’ matches the name used in ‘paths’ above.

Updating references

Now that we have configured RequireJS, technically all we need to do is load RequireJS using the configuration and all should be fine!

Well, let’s update the Index.html file to load RequireJS and remove all other Script references. We update the script references as follows:

<!–<script src=”Scripts/jquery-1.9.0.js”></script>
<script src=”Scripts/bootstrap.js”></script>
<script src=”Scripts/knockout-3.2.0beta.debug.js”></script>–>

<script src=”App/boot/require.config.js”></script>
<script src=”Scripts/require/require.js”></script>

Now you are wondering where is the greeting.js gone and how is it going to be loaded? Well there is no magic, we have a couple of more steps to go.

Add App/boot/Startup.js

In the App/boot folder add a new JavaScript file called startup.js. The name is to help us understand the process, it’s not a convention.

Add the following ‘module-definition’. You can read up about RequireJS Modules here.

define(['jquery', 'knockout', 'bootstrap'], function ($, ko)
{   
    ko.components.register(‘greeter’,
    {
      require: ‘App/components/greeter/greeting’
    });
    ko.applyBindings();
});

The Startup module says that it is dependent on jQuery, KnockoutJS and BootStrap. Note, it uses the same names that were used in RequireJS configuration above. The function parameters are instances of the dependencies requested in the array, so if you put another input parameter like boots it would have instance of the bootstrap library. We’ll just keep this in mind for now.

Next it declares a function that has jQuery and KO lib references as input parameters.

In the function we ‘register’ our ‘greeter’ component. Note, that we have moved the registration from the greeting.js to startup. Also note instead of specifying the hard-coded template and view model, we are simply configuring a ‘require’ property, that points to the folder where the greeting.js is (without the js).

Well, that’s the startup configuration. Needless to say, as we add more components they will need to be registered here.

Updating our ‘greeter’ component

The first thing we do is add a greeting.html (name same as JavaScript is again not a convention, just easier to map in our heads).

It contains the same markup that we had hardcoded in the template:

image

Update greeting.js

Finally we update the greeting.js component. We comment out all the old code and replace it with the the following:

define(["knockout", "text!./greeting.html"], function (ko, greeterTemplate) {   
   
function greeterViewModel(params) {
        var self = this;
        self.greeting = ko.observable(params.name);
        self.date = ko.observable(new Date());
        return self;
    }
    return { viewModel: greeterViewModel, template: greeterTemplate };
});

So essentially we have morphed our component to a RequireJS module. Key thing to note here is use of the text plugin to load load the greeting.html. Require does all the work to load the template and stuff it into the greeterTemplate parameter.

Finally we return an object that KO accepts as definition for a module.

One more thing!

We are almost there. Those paying close attention would have noticed that we didn’t put in reference to the startup.js anywhere. How does RequireJS know how to initialize our app and it’s dependencies?

Back to the Index.html, we update the script tag that refers to RequireJS as follows:

<script data-main=”App/boot/startup” src=”Scripts/require/require.js”></script>

The data-main tag, tells Require that the main module to initialize the app is in that JS file. Thus RequireJS knows what to invoke once fully initialized.

Done!

Conclusion

If you run the application now, you’ll see the same old screen we saw in the first part. So what have we done new? Well plenty:

1. Let RequireJS load scripts dynamically.
2. Separated our KO components’ view from it’s model
3. Defined a central place to register all KO components

What we have not done is do more ‘webby’ stuff like putting in links to other possible pages of the app and creating different types of modules for each page and then showing how our app can navigate to those pages as well as load those dependencies on-demand. That’s what we’ll cover in the next part – Routing!

Source Code

The source is on Github as usual (note, after each article I am branching the code out and keeping the master for the latest updates)!

https://github.com/sumitkm/BuildingSpaUsingKO/tree/Part2

Tagged , , ,
Follow

Get every new post delivered to your Inbox.

Join 1,185 other followers

%d bloggers like this: