Deploying your Durandal 2.0 SPA to production – Some Notes

Standard

Durandal 2.0 has some breaking changes from its earlier version.  If you are converting from 1.x to 2.0, read this documentation first.  I haven’t converted my project that is currently in production (nepris.com) into 2.0 yet. I wanted to figure out 2.0 first before I jump into that change. So, for a different smaller application, I started with Durandal 2.0. I used Durandal Starter Kit with Visual Studio 2012 to get the base template up. While developing locally, everything ran pretty well, but ran into some issues with building it for production. Here are some notes… (BTW, I like 2.0 already, feels much more clean and stable than 1.x)

  1. Durandal 2.0 uses a new build system, it uses weyland. I followed the instructions on Durandal site (link) . It failed !! – Issue is in the weyland-config.js in the root of your project. Open this config file and delete the line it saying  ** mainConfigFile:’App/main.js’ This will get you past this error.
  2. If you are working with Visual Studio, you can automate this build – Follow this instructions  (Durandal docs are getting better day by day :)
  3. For every production deployment, you still need to bust the cache using the following:
    requirejs.config({
        paths: {
            'text': '../Scripts/text',
            'durandal': '../Scripts/durandal',
            'plugins': '../Scripts/durandal/plugins',
            'transitions': '../Scripts/durandal/transitions'
        },
        urlArgs: "bust=v0.11"
    });
    
    //In your View
    @if(HttpContext.Current.IsDebuggingEnabled) {
      <script type="text/javascript" src="~/Scripts/require.js" data-main="App/main"></script>
    }
    else{
       <script type="text/javascript" src="~/App/main-built.js?v=0.11"></script>
    }
    

Rest of the stuff worked pretty straight forward. I will be converting my 1.x application to 2.0 soon, will post once everything gets done.

Cheers!

Integrating DISQUS to your SPA

Standard

I am building a single page application (SPA) using Durandal JS (with ServiceStack powered API layer). In one of the sections I wanted to have DISQUS discussion integrated. This blog post outlines problems I faced and solution to the problem I found.
In a normal web site, integration of DISQUS to your pages is easy, you sign up for DISQUS, get the JavaScript Universal Code for web sites, add it where you want your discussion thread to show up in your page. When web page loads, DISQUS loads the required Javascript and adds it to the section of your page. By default DISQUS will use the page url as the key to the discussion and manages unique thread ids for your posts and replies within that.

Now with this model we have a problem, in a SPA, there is no “pages” its all one “single” page with routes (by some convention) managing the loading of various views. So if you simply paste DISQUS code where you want the discussion to show up in your SPA view html page, result will be disappointing! At least it was for me the first time. Again to my defense, it was late in the night and I was not thinking clearly. When the page ran, it did not load anything…blank! Problem is that if you add a JS snippet to your view code, its not going to work in a SPA architecture.

Solution

We need to load the required JavaScript file just like you load any other piece of code that is required by your application. Here  is how you do it…

     function initDisqus() {
	window.disqus_shortname = 'your-app-name';
	var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
		dsq.src = '//' + window.disqus_shortname + '.disqus.com/embed.js';
		$.getScript(dsq.src)
			.done(function () {
		          //
			});

	}

This works, well mostly…If you run your page now, you will see your discussion initialized on the page. And you can start adding comments, I was happy to get to this point, and soon realized that all the comments were getting added to the root of the site, and if you reload the page, DISQUS failed to attach the comments back to this discussion. All those comments were kinda orphaned. Soon I realized that the presence of ‘#’ (hash) in my url is also messing up with how DISQUS works!! If you have a URL like http://www.mysite.com/#/blog/my-first-blog, DISQUS does not treat anything after the ‘#” as part of your Url and attaches the thread id after the ‘#’.

So what do you do? One way (at least what I think) is to change ‘#’ to ‘#!’, for example, the URL given before will change to http://www.mysite.com/#!/blog/my-first-blog. Durandal did not complain and loads the page, and DISQUS seems to like it too. One another thing you will need to do is to reload DISQUS every time your new view loads with new unique identifier for the page and the link. This way all the discussions that happens will be tracked under your unique view (urls).

More complete solution:

     function initDisqus() {
	window.disqus_shortname = 'your-app-name';
	var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
	dsq.src = '//' + window.disqus_shortname + '.disqus.com/embed.js';
	$.getScript(dsq.src)
			.done(function () {
				DISQUS.reset({
					reload: true,
					config: function () {
						this.page.identifier = window.location.href;
						this.page.url = window.location.href;
					}
				});
			});

	}

Note: Make sure to call this function from your “viewAttached” within your view model code.

That’s all for now folks, Happy programming!

Cheers!
Binu

Deploying Durandal SPA to Production

Standard

***EDIT – This post talks about Durandal 1.x ***
I have been building a single page application (SPA) using Durandal JS. In this post, I want to talk about some of the steps that you want to take when you are deploying your single page application to your production/live web server. In my case I am using ASP.Net MVC 4 and I use Windows Azure to host my website. If you want to learn more about how to build a SPA using Durandal, I strongly suggest you watch John Papa‘s SPA Jump Start course in Pluralsight.

Typically your SPA application will have a structure like this:

In this all the files except the main-built.js is your application. This includes Durandal, Require JS, your files, etc. “main-built.js” is the optimized single file that contains your javascript and html files that you need to deploy to your production web server. So you do you package this for production? This is simple than I thought. I was thinking of using ASP.Net Bundle capability to bundle all the JS files, but decided against it because I felt to manage my SPA files using ASP.Net bundling could become very tricky very fast because of paths, and html files. So I went with the optimizer that is included with Durandal.

Once you install durandal using Nuget ( or HotTowel Nuget), you will see under App\durandal\amd\ you have a file called optimizer.exe. This is the tool we will use to package our SPA application.

  1. Step1: To use this you will need to install Node.js, So get node.js and install. You can download Node.js from here.
  2. Click on Start > All Programs > Node.js > Node js command prompt
  3. Navigate to your projects \app\durandal\amd folder
  4. Run optimizer.exe
  5. This will scan everything under your \App folder and creates the combined and optimized file main-built.js

Now we can use this file in our application, following code snippet shows how to include this in your index.chtml file. We will use the full files when we are in debug mode and when we are running a release configuration, use the optimized file.

@Scripts.Render("~/scripts/vendor")
@if(HttpContext.Current.IsDebuggingEnabled) {
     <script src="~/App/durandal/amd/require.js" data-main="App/main"></script>
}
else
{
    <script type="text/javascript" src="~/App/main-built.js"></script>
}

As you can see, I am using ASP.Net Bundles to combine and compress all the standard JS files like bootstrap, jquery plugins, or any other components that I use and are not part of my SPA code and use the optimizer created file for my SPA code. With this approach, our application code (SPA code) is send to browser as a single file enabling faster download. If you want to bust cache when each time you are deploying new code to your server, you can configure Require.js to include a query string parameter to each resource it fetches.

require.config({
	paths: { "text": "durandal/amd/text" },
	urlArgs: "bust=" + (new Date()).getTime()
});

Reference: http://requirejs.org/docs/api.html#config.

Please note that the code snippet above is ideal only for development, for your release code, change this to something like :

urlArgs: "bust=v1.1"

This will make sure that your users will not be downloading any resources that browser already has unless it is changed (which typically happens when you have a new release)

That’s all for now folks! Happy Programming!
Cheers!
Binu