Simple client side routes and content loading in Umbraco

Do you like your users to be able to load subcontent on a page dynamically and support client side url’s (which in turn enables browser history, url-copy&pasting and bookmarks)? In this post I will show how you can do it in a very flexible way with the help of Backbone.js and a minimum of code.

This is what I’m trying to achieve, I have an ordinary page with links to “sub content”, which is supposed to appear on the same page when the user clicks on a link:

When the user clicks “sub content 2” the url becomes some-page#subcontent-2 and the corresponding content shows:

To use “client side routes” we need to add some library that handles such. The most commonly used one is probably Backbone.js.

To use it you will need to add script reference to Backbone.js and to Underscore.js which it has as a dependency.

Download the files or add reference to CDN-paths, for example:

http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js
http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js

Defining our routes and load the asked for content.

A client side route url part is everything in the url that comes after a hash, i.e. “/some-post#myview/whatever” => “myview/whatever”.

Backbone is using a straight forward syntax to setup routes, where every route has a url matching route string and a function to call. It handles parameter url parts aswell. The syntax to use a parameter is adding a colon before its name i.e. “myview/:id”

In our case we’ll use “:urlName” to match every url (“#subcontent-1” and “#subcontent-2”) and pass the url string as the parameter urlName to the route function subContentsRoute.

The subContentsRoute route function does only one thing : it loads the html content and puts it in the #subcontents div. It’s simply using a fixed url part + the urlName parameter value (i.e. “/some-post/subcontents-2”).

var SubContentRouter = Backbone.Router.extend({
        routes: {
		":urlName" : "subContentsRoute",
           	"*actions": "defaultRoute" // matches http://example.com/#anything-here
        	},
        subContentsRoute: function(urlName) {
		$.get("/some-post/" + urlName, function(data){
			$("#subcontents").html(data);
		});
	},
        defaultRoute: function( actions ){
            this.subContentsRoute('subcontent-1');
        }
    });

$(function(){
    var app_router = new SubContentRouter;
    // Start Backbone history a neccesary step for bookmarkable URL's
	Backbone.history.start();
});

Save it as subcontentBackboneApp.js and add a script reference into the html template :

<script type="text/javascript" src="/scripts/subcontentBackboneApp.js"></script>

The rest is common Umbraco usage – create documenttype, template, content and a small macro

How to make /some-post/subcontent-2 return the right content html? Let’s setup Umbraco with a simple document type with only bodyText as a richtext property, and a template that only returns the html we like to add into our div tag.

With a document and a template in place we add our documents:

Lastly we need to create those client side links and add a container for our dynamic content. We do that by adding this macro code in the original textpage template:

      <umbraco:Macro runat="server" language="cshtml">
      @foreach(var child in Model.Children)
	     {
	        <a href="#@child.UrlName">@child.Name</a> 
	     }
	  <div id="subcontents"></div>

Can I haz images and macros?
Our subcontent can be anything, styles, tags, images. But if you use a macro, the macro context will be the subcontent node and not the containing node.

Flexibility
I find this simple approach with a router to be suitable for several similar usages to what I’m showing in this example.
* Loading “more content”,
* Loading “next sub article”,
* Being the base for a small single page application within an Umbraco site

Sample with Backbone model and view

$(function(){

	var subContentModel = new Backbone.Model({
		htmlContent:""
	});
	
	var SubContentView = Backbone.View.extend({
		initialize : function () {
			this.model.on("change",function () {
				this.render();
			}, this);
		},
		render : function() {
			this.$el.html(this.model.get("htmlContent"));
			return this;
		}});

	var subContentView = new SubContentView({
		model: subContentModel, 
		el:'#subcontents',
		});
	
	var SubContentRouter = Backbone.Router.extend({
        routes: {
			":urlName" : "subcontents",
            "*actions": "defaultRoute" 
        },
		subcontents: function(urlName) {
			$.get("/some-post/" + urlName, function(data){
				subContentModel.set({htmlContent:data});
			});
		},
        defaultRoute: function( actions ){
            this.subcontents('subcontent-1');
        }
    });
	
	
	var subContentRouter = new SubContentRouter;
	Backbone.history.start();

});
Advertisements

One thought on “Simple client side routes and content loading in Umbraco

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s