Using Durandal with ASP.NET 5

Attribution: https://www.flickr.com/photos/jalbertbowdenii/8703333761/

Durandal is the perfect blend of various client-side libraries for building a robust web application. For those developers that know me, they know I’m a big fan of KnockoutJS, which Durandal relies on heavily. If you haven’t heard of KnockoutJS, it’s a library for creating data-bound views. It adds data-binding code unobtrusively into your HTML to observable properties on your viewmodel. When the viewmodel property changes, your view automatically updates to reflect those changes, as well as updating the viewmodel when a value changes, or an event takes place on your view.

ASP.NET 5 has built in support for popular tools that have up till now been off limits or at least hard to integrate for .NET developers. Tools like bower, grunt, gulp, and npm make a web developers life better by providing features such as client-side package management, minification, and transpiling.

I’m going to show you had to easily take advantage of the new ASP.NET 5 features to create a new Durandal web application. We’ll start by ensuring that we have the latest ASP.NET 5 version. As of the time of writing this article ASP.NET 5 Beta 8 was the latest version. Follow these instructions to ensure you correctly install the runtime.

Once you are up-to-date with ASP.NET 5. Open up Visual Studio and create a new project by selecting Web -> ASP.NET Web Application

Create Project

A second pop-up dialog will show, and from there you’ll select ASP.NET 5 Preview Templates -> Empty. We’ll do this so that we don’t get a bunch of unnecessary things in our way to confuse us.

Empty Project Template

We should now have a new project created that is mostly empty. If you haven’t used the latest version of ASP.NET yet, the project structure may seem strange to you. There are several new features that make developing client-side apps easier, but require configurations of their own which you’ll see in your project. The new project structure doesn’t use a web.config, but instead uses a project.json file to store it’s dependencies, configuration, startup commands, version info etc…

You can read more about this new project structure on Microsoft’s ASP.NET 5 website. But for now let’s go ahead and open up the project.json file and add a new NuGet package. Under the dependencies section of the json file, add "Microsoft.AspNet.StaticFiles": "1.0.0-beta8" Your file should look something like this.

Dependencies

You’ll notice that Visual Studio provides Intellisense which makes choosing packages and their versions very easy. The StaticFiles package enables us to host static files with our website. This is necessary because out of the box, ASP.NET will not server our HTML files and since we’re building a client-side app, there is no need to use MVC just to setup the one page we need to host our entire site. All of our other pages will be loaded dynamically by Durandal via its built in router. Now that we’ve added this package, we need to tell ASP.NET to enable static file hosting. We can do this in our Startup.cs which is sort of like the old Global.asax files. Delete the example template code that writes out “Hello World!” to the response pipeline and replace it with app.UseFileServer();. This is all we need to do to enable static file hosting.

Startup Code

Now that we have ASP.NET all setup, we need to go about setting up Durandal. We first will need to install all of the client-side dependencies we need. Luckily Bower is supported with ASP.NET 5 and Visual Studio 2015. Bower is a client-side package manager and makes importing libraries as simple as adding new NuGet packages. All we need to do to enable it in Visual Studio 2015 is add a bower.json file. Let’s do that now.

Bower

Now that we have bower enabled, let’s open the bower.js file and add jquery 2.1.4 or greater and durandal 2.1.0 or greater. Again, you’ll notice that Visual Studio provides Intellisense for packages and their versions.

Bower Dependencies

After adding the dependencies make sure you save and build the project, this will tell bower to install (or restore) the packages to the folder configured in the .bowerrc file (which was automatically added and should be wwwroot/lib). You may also notice that bower will install any dependencies of the packages you choose. In our case bower installed requirejs and knockoutjs which are both dependencies of Durandal.

Let’s go ahead and create our main index.html file that will host our entire app. Create an index.html in the wwwroot folder and add the following code to it.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>My Durandal Site</title>
    <link rel="stylesheet" href="lib/durandal/css/durandal.css" />
</head>
<body>
    <div id="applicationHost"></div>
    <script src="lib/requirejs/require.js" data-main="app/main"></script>
</body>
</html>

You’ll notice that we added the default durandal stylesheet, which just adds some basic styling for modals and transitions. You will add any stylesheets you create for your site in this file as well. Notice that we added a single div with the id of “applicationHost”. This is where durandal will render the rest of our app. We also add our one and only script reference for requirejs. Since Durandal relies on requirejs for module loading, it’s the only javascript reference we need to add by hand. It also looks for the data-main attribute which tells requirejs that it should look for a javascript file named “main” in the “app” folder for the rest of it’s configuration.

So let’s go ahead and create that app folder in wwwroot and add a main.js file to it. The contents of the main.js file should look something like this.

requirejs.config({
    paths: {
        'text': '../lib/requirejs-text/text',
        'durandal': '../lib/durandal/js',
        'plugins': '../lib/durandal/js/plugins',
        'jquery': '../lib/jquery/dist/jquery',
        'knockout': '../lib/knockout.js/knockout'
    }
});

define(['require', 'durandal/system', 'durandal/app', 'durandal/viewLocator', 'jquery'], function (require, system, app, viewLocator, $) {
    system.debug(true);

    app.title = 'Durandal App';

    app.configurePlugins({
        router: true,
        dialog: true
    });

    app.start().then(function () {
        viewLocator.useConvention();

        app.setRoot('viewmodels/container');
    });
});

The first thing we’ll do is tell requirejs where to look for our client-side dependencies and what their aliases are. After that we’ll create a single module and load in various dependencies we’ll need to properly initialize our Durandal app. Durandal is built around a very robust plugin system and you should get familiar with it if you’re going to be doing serious coding in Durandal. I’d recommend you read up on custom knockout bindings as well since you’ll need them to integrate knockout with other libraries, but you may get by with using other peoples bindings they’ve already written. You’ll still need to know how to add them as Durandal plugins though. The last part of this file will initialize the app and tell Durandal where the container viewmodel as (and matching view).

We’ll need to create a views and viewmodels folder in our app folder since Durandal will uses these folders as the convention to locate our view and viewmodels. Lets also create a container.html view and a container.js viewmodel.

Container View Viewmodel

In the container.js viewmodel, you’ll have the following code.

define(['plugins/router', 'knockout'], function (router, ko) {
    // This will setup routing at the root level and tell 
    // Durandal what the default route is using ['', 'home'] syntax.
    var rootRouter = router
        .makeRelative({ moduleId: 'viewmodels' })
        .map([
            { route: ['', 'home'], moduleId: 'home', title: 'Home Page', nav: true }
        ]).buildNavigationModel();

    // We must activate the router before it can be used for routing.
    rootRouter.activate();

    // Create a viewmodel function (this is our container viewmodel) and
    // create a property for the router, the view will use this property
    // to compose the requested page based in the hash value (#home).
    return function () {
        var self = this;

        self.router = rootRouter;
    };
});

This will setup our root router and our single view and viewmodel for our home page. This is also where you’ll add additional routes for the rest of your site when you add new pages. You’ll need to add the following code to your container.html view so that Durandal will know where to render each page’s contents.

<div id="container">
    <header>Durandal App</header>

    <div id="page" data-bind="router: { cacheViews:false }"></div>

    <footer>Example Footer</footer>
</div>

Here we just have one div that has a data-bind attribute which includes the router plugin that will handle composing each sub page. Finally, we’ll need to add our home page view and viewmodel to their respective folders.

Home View Viewmodel

In the home.js viewmodel, you’ll have the following code which just sets up a single example property that contains the string “Hello World!”. This is just to give you a simple example of the knockout binding between view and viewmodel.

define(['knockout'], function (ko) {
    return function () {
        var self = this;

        self.message = ko.observable('Hello World!');
    };
});

The home.html view will have the following HTML.

<div id="home">
    <h1 data-bind="text: message"></h1>
</div>

Now that we have the basic skeleton of our site setup, you should be able to run your site by selecting the “web” command from the run menu (or you can use IIS Express or IIS). You can select the drop down next to the run menu and select the web command (which is configured in your project.json file).

Web Command

Running the web command will start a web server listening at http://localhost:5000. Assuming everything was done correctly, you should see the following site render in your browser of choice.

Web Render

You’ll find that Durandal and ASP.NET 5 work together very well and make for a very comfortable and powerful development experience. I’d love to hear your thoughts about this article, or these technologies in the comments below.

Please follow and like us:
RSS
Follow by Email
Facebook
Google+
http://dylanvester.com/2015/11/using-durandal-with-asp-net-5/
Twitter
SHARE

You may also like...