Tag Archives: Durandaljs

Super Easy SPA with Durandal

I have not been very active from almost one and half year on this blog. There has been lot of learning, unlearning had happened in this duration. By the way, to revive my blog, I have invited my good friend Akhlesh Tiwari to share his overall JavaScript expertise with all of us. He happily agreed and here is the first article on his favorite subject i.e. Single Page Applications. Please do encourage the new author and share your feedback so that we all can improve.

Durandal is open source JavaScript library for SPA. Durandal is built on libs like jQuery, Knockout and RequireJS. So if you are already familiar with these libraries then very easily you can start making amazing single page apps.

Durandal is technology agnostic SPA framework so you can use it with any backend technology or make pure html/JavaScript app. To get start with Durandal all you need to get JavaScript libraries and modules and follow folder structure. So for this tutorial we take asp.net MVC4 as backend.

Get Start with Durandal in .Net

In .net we can download Durandal template from  VSIX file . Durandal is also available through nuget or you can install Durandal starter kit with this command Install-Package Durandal.StarterKit .but I will use manual setup for Durandal for better understanding of the framework. For manual setup just download all startup files. Startup project has basic examples in Durandal and navigation setup that can be modified as per requirement. After downloading startup project, we will go step by step to create Durandal app.

Step-1 Create MVC Project

First we will create mvc4 internet application in visual studio named as DurandalApp.

New Project

You can also take empty MVC project but then you have to write your own controller and starting cshtml page.

Step-2 Folder Structure

Durandal follows the folder structure to create application so here is application organization:-

Durandal Folder Structure

I recommend creating “App” folder with in project as shown above. Durandal applications are built as a collection of AMD modules. In fact, Durandal itself is just a set of modules. Here is what each folder used for:-

“viewmodels folder”- contains application-specific code (js file)

“views folders” contains the application-specific views(html).

“main.js” contains all of the JavaScript startup code for your app including the route configuration, module configuration etc. your app execution always starts with main which is referenced by the RequireJS script tag in the index.html file (.cshtml for .NET).

After setting up this folder structure, copy Durandal library under Script folder of your MVC project (You can keep Durandal library anywhere as per requirement, I keep it in Script folder because it just like a 3rd party JavaScript library). Durandal library has all core modules.

Durandal library folder structure

Durandal Lib Folder Structure

We also need RequireJS and knockout js(Jquery is optional) so I have added two more folders under lib folder. These folders have respective js library.

Step-3 Index.cshtml

A Durandal app is a Single Page App. When you navigate between pages, you are not navigating to new pages on the server. Instead, you are loading new virtual pages into the one-and-only-one server-side view(index.cshtml). For this sample I have created MVC HomeController.

namespace DurandalApp.Controllers{

public class HomeController : Controller{

public ActionResult Index(){

return View();

}

}

}

So it is just typical MVC code. I created the following server-side MVC view. This is the one-and-only server-side view used by the application.

Index.cshtml

<div id=”applicationHost”>

<div>

<div>

Sample App Durandal

</div>

<i></i>

</div>

</div>

<script type=”text/javascript” src=”../Scripts/lib/require/require.js” data-main=”/App/main”></script>

The “applicationHost” is where your app’s views will live. Below that is the script tag that references RequireJS. It points to our application’s entry point, declared in the data-main attribute. At runtime, this resolves to the main.js file.

In layout.cshtml we will setup css and js libraries. @RenderBody() has nothing special.it just renders the cshtml view.

<!DOCTYPE html>

<html lang=”en”>

<head>

<link rel=”stylesheet” href=”Content/bootstrap/css/bootstrap.css” />

<link rel=”stylesheet” href=”Content/bootstrap/css/bootstrap-responsive.css” />

<link rel=”stylesheet” href=”Content/font-awesome/css/font-awesome.css” />

<link rel=”stylesheet” href=”Scripts/lib/durandal/css/durandal.css” />

<link rel=”stylesheet” href=”Content/site.css” />

</head>

<body>

@RenderBody()

</body>

</html>

You can see that I have put all style sheets under content folder and JavaScript under Scripts folder. In Durandal app durandal.css is used to render message box and dialog box and other css are optional.

Step-4 main.js

In 4th step we will see entry point of app that is main.js. It is the first code that get executed and where you can configure Durandal setting and tells to start the application.

main.js

requirejs.config({

paths: {

‘text’: ‘../Scripts/lib/require/text’,

‘durandal’: ‘../Scripts/lib/durandal/js’,

‘plugins’: ‘../Scripts/lib/durandal/js/plugins’,

‘transitions’: ‘../Scripts/lib/durandal/js/transitions’,

‘knockout’: ‘../Scripts/lib/knockout/knockout-2.3.0’,

‘bootstrap’: ‘../Scripts/lib/bootstrap/js/bootstrap’,

‘jquery’: ‘../Scripts/lib/jquery/jquery-1.9.1’

}

});

define([‘durandal/system’, ‘durandal/app’, ‘durandal/viewLocator’], function (system, app, viewLocator) {

system.debug(true);

app.title = ‘Durandal Starter Kit’;

app.configurePlugins({

router: true,

dialog: true,

widget: true

});

app.start().then(function () {

viewLocator.useConvention();

app.setRoot(‘viewmodels/shell’, ‘entrance’);

});

});

This code shows main functionality but it can be different according to application. Here is the list of main task in main.js:-

  1. RequireJs: – Configuration:- first few lines of main js used for configuring the durandal module path.
  2. Debugging: – Durandal have debugging functionality which can be used by turning on the debugging.
  3. Title: – You can set your application title.
  4. ConfigurePlugins: – here you can tell which plugin will available to your app.
  5. app.start():- this is actually used to kick off the application and it returns a promise which resolved when DOM is ready and framework is prepared for configuration.
  6. UseConvention: – here we set up our viewLocator with basic conventions.
  7. app.setRoot():- This is what actually causes the DOM to be composed with your application. It points to your main view model (or view). When this is called, Durandal’s composition infrastructure is invoked causing RequireJS to require your root view model, use the viewLocator to locate its view, data-bind them together and inject them into the applicationHost element. Additionally, the ‘entrance’ transition animation is used to animate the app in.

Step-5 Shell

shell is just like master page of durandal app where put all static content which you want to remain constant throughout the durandal app. So this is great place to have navigation, header and footer etc.  So first we will create the shell.js in App/viewmodel and shell.html in App/views.

shell.js

define([‘plugins/router’, ‘durandal/app’], function (router, app) {

return {

router: router,

activate: function () {

router.map([

{ route: ”, title: ‘home’, moduleId: ‘viewmodels/home’, nav: true },

]).buildNavigationModel();

return router.activate();

}

};

});

Shell.html

<div>

<div>

<div>

<ul data-bind=”foreach: router.navigationModel”>

<li data-bind=”css: { active: isActive }”>

<a data-bind=”attr: { href: hash }, html: title”></a>

</li>

</ul>

</div>

</div>

<div data-bind=”router: { transition:’entrance’ }”></div>

</div>

When you call setRoot, Durandal requires both the module and the html and uses Knockout to data-bind them together. It then injects them into the DOM’s applicationHost. As we see in shell.js, router plugin is used for registering the route.then we have used router’s navigationModel to dynamically generate or link in shell.html.

Here in shell.html knockout is used for view composition i.e. router keep track of current route and when route is changed new view is composed according to new route. Here is how exactly it happens?

  1. A route is triggered and the router finds the corresponding module and sets it as active.
  2. The router binding detects that the active module has changed. It examines the value and uses that to find the appropriate view (using the viewLocator).
  3. The module and the located view are data-bound together.
  4. The bound view is inserted into the DOM at the location of the router binding.
  5. If the router binding specifies an animation, it is used to smoothly show the new view.

Step-5 View and Viewmodels (Final Step)

Each page in application is comprised of view and viewmodel. Once you’ve setup the application as described above you can extend the application by adding new view in views folder and viewmodel in viewmodels folder. Then you just register with router in shell.js and when you go to that route, router will locate viewmodel and compose the view(insert the view in DOM). For example I am taking new page home.html.

So first create test.html under views folder and with the same name create js file under viewmodels folder.

home.js

define(function (require) {

var app = require(‘durandal/app’);

return {

displayName: ‘home Page’,

showMessage: function () {

app.showMessage(‘This is my first home page!’);

}

};

});

home.html

<div>

<h2 data-bind=”html: displayName”></h2>

<button data-bind=”click: showMessage”>Click Me</button>

</div>

Finally, go to the shell.js module and add update the router’s mappings whenever you add new page. In our application we have already added router mapping for home page.

define([‘plugins/router’, ‘durandal/app’], function (router, app) {

return {

router: router,

activate: function () {

router.map([

{ route: ”, title: ‘home’, moduleId: ‘viewmodels/home’, nav: true }

]).buildNavigationModel();

return router.activate();

}

};

});

Note-when you add new page, for registering your route, update the route mapping under shell.js. route property will have different value according to route.

e.g. If you add two new page like about and contact then route.map will have :-

router.map([

{ route: ”, title: ‘home’, moduleId: ‘viewmodels/home’, nav: true },

{ route: ‘about’, title: ‘about’, moduleId: ‘viewmodels/about’, nav: true },

{ route: ‘contact’, title: ‘contact’, moduleId: ‘viewmodels/contact’, nav: true }

]).buildNavigationModel();

Now, run the application (make sure your browser isn’t caching resources) and you should see a new navigation option called ‘test’. Click on it and you will navigate to your new page. It’s that simple. I hope you like this blog post and will now start your journey of SPA-Single Page Applications.