Mustache Client and Server Templates

Heads Up!

This article is several years old now, and much has happened since then, so please keep that in mind while reading it.

Modern websites take advantage of both client side Javascript and server side code to create the best experience. It's easy for this to add some maintenance headaches as you may now need to maintain 2 different code bases.

I'm going to show you how we make things a bit simpler by using the Mustache templating engine to use the same templates when generating html on the client and the server. I've chosen Mustache primarily because it's very simple and has good client and server side implementations.

I'm taking the starter site that comes with Umbraco. On the blog page there is a TODO to implement paging, so that seemed a good place to start. I'm going to do the simplest version of paging just implementing a load more button to load a new page every press until there are no more pages, at which point I'll remove the button.

I plan on tackling this as follows:

Step 1: Refactor the existing server side implementation to make it simpler to add support for a client side implementation.

Step 2: Create a client side Mustache implementation that allows for additional blog posts to be fetched from the server.

Step 3: Update the server side implementation to share the client side Mustache templates on the server.

Step 1

The first thing we're going to do is make a new model to store just the information that is required to show the blog posts on the blog page. I'm starting with a new model rather than the one provided by Models Builder because it will need to be serialised and it will be sent as an Ajax request so I want it to contain just the required information. We'll call it BlogPostSummary to differentiate it from the regular blog post model and make it clear that it's not the full blog post.

using System.Collections.Generic;

namespace MustacheDemo.Models
{
	public class BlogPostSummary
	{
		public BlogPostSummary(Blogpost post)
		{
			Url = post.Url;
			CreateDate = post.CreateDate.ToShortDateString();
			PageTitle = post.PageTitle;
			Excerpt = post.Excerpt;
			Categories = post.Categories;
		}
		public string Url { get; set; }
		public string CreateDate { get; set; }
		public IEnumerable<string> Categories { get; set; }
		public string PageTitle { get; set; }
		public string Excerpt { get; set; }
	}
}

We'll also create a new model to represent a single page of the Blog, as well as indicate if there are more posts available.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MustacheDemo.Models
{
	public class BlogPage
	{
		public IEnumerable<BlogPostSummary> BlogPosts { get; set; }
		public bool HasMore { get; set; }
	}
}

Next we'll create a very basic service to make it easy to fetch a BlogPage containing an enumeration of BlogPostSummary to represent a single page of the blog. 

using System.Linq;
using MustacheDemo.Models;
using Umbraco.Core.Models;

namespace MustacheDemo.Services
{
	public class BlogPageService
	{
		public BlogPage Page(IPublishedContent parent, int page = 1, int perPage = 5)
		{
			var posts = parent.Children
				.OrderByDescending(x => x.CreateDate)
				.Skip((page - 1) * perPage).Take(perPage + 1)
				.ToList();

			return new BlogPage
			{
				BlogPosts = posts.Take(perPage).Select(p => new BlogPostSummary(new Blogpost(p))),
				HasMore = posts.Count > perPage
			};
		}
	}
}

We'll now update the blog template to use this service to get its blog posts and output them rather than use the macro that comes with the starter site. First create a new partial in the shared folder called BlogPage.cshtml.

@model MustacheDemo.Models.BlogPage
@foreach (var post in Model.BlogPosts)
{
	<a href="@post.Url" class="blogpost">
		<div class="blogpost-meta">
			<small class="blogpost-date">@post.CreateDate</small>
			<small class="blogpost-cat">
				@foreach (var category in post.Categories)
				{
					@category
				}
			</small>
		</div>
		<h3 class="blogpost-title">@post.PageTitle</h3>
		<div class="blogpost-excerpt">@post.Excerpt</div>
	</a>
}

We'll also delete the LatestBlogposts.cshtml file from the MacroPartials folder that comes with the starter site.

Finally we'll update the blog.cshtml template to use this new partial and the new BlogPageService we've just created.

@inherits UmbracoTemplatePage<ContentModels.Blog>
@using MustacheDemo.Services
@using ContentModels = Umbraco.Web.PublishedContentModels;
@{
	Layout = "Master.cshtml";
	var blogModel = new BlogPageService().Page(Model.Content);
}
@Html.Partial("~/Views/Partials/SectionHeader.cshtml")
<section class="section">
	<div class="container">
		<div class="blogposts">
			@Html.Partial("BlogPage", blogModel)
		</div>
	</div>
</section>

Spinning the site up you should be able to see that the blog page is still working as before. If so we can be confident that the refactoring is complete and we haven't broken anything on the site. Although we haven't changed the functionality we have put the foundations in place to support the new client side implementation by moving the code that fetches the blog posts to a service allowing the same logic to be used in more than one place.

Step 2

Now we want to enable the client side to be able to get hold of blog posts. First off we'll need to expose the BlogPageService through a new WebApiController. Umbraco provides a base class for this called UmbracoApiController. So we'll add a new BlogPageController inheriting UmbracoApiController to the site. (I've hardcoded the blog page id in this to keep the code simple, don't do that on production)

using MustacheDemo.Models;
using MustacheDemo.Services;
using Umbraco.Web.WebApi;

namespace MustacheDemo.Controllers
{
	public class BlogPageController : UmbracoApiController
	{
		[System.Web.Http.AcceptVerbs("GET")]
		public BlogPage Page(int page = 1, int perPage = 5)
		{
			var startNode = Umbraco.TypedContent(1122);
			return new BlogPageService().Page(startNode, page, perPage);
		}
	}
}

This will automatically be setup a route by Umbraco at at /umbraco/api/blogpage/page?page=<pagenumber> to test this we can go to that URL to see if the results show and it's all working correctly. This should return one page at a time of blog post summaries as JSON or XML.

Next we'll need some Javascript to call the API endpoint, convert the page of blog posts to HTML and then insert that into the page. To do this I've downloaded the Javacript Mustache library to enable us to use it to render templates for the blog. I've added a reference to this in the Master.cshml file.

After the blog posts are initially loaded we'll add a new "load more" link to load the next set of results. So we'll add this to the bottom of the razor blogposts.cshtml partial template we created earlier:

@if (blogModel.HasMore)
{
  <a href="#" class="loadmoreblogposts">Load more</button>
}

Mustache will need a template which it will use to render the page of blog posts, so we'll create a Mustache version of the updated BlogPosts.cshtml template at the bottom of the blog.cshtml section inside a script tag like this.

<script id="blogPageTemplate" type="x-tmpl-mustache">
	{{#BlogPosts}}
	<a href="{{Url}}" class="blogpost">
		<div class="blogpost-meta">
			<small class="blogpost-date">{{CreateDate}}</small>
			<small class="blogpost-cat">
				{{#Categories}}
				{{.}}
				{{/Categories}}
			</small>
		</div>
		<h3 class="blogpost-title">{{PageTitle}}</h3>
		<div class="blogpost-excerpt">{{Excerpt}}</div>
	</a>
	{{/BlogPosts}}
	{{#HasMore}}
	<a class="loadmoreblogposts" href="#">Load more</a>
	{{/HasMore}}
</script>

If you're not familiar with Mustache there is some documentation to help you get up to speed (Unfortunately the .NET implementation only supports version 1 so we'll stick to that)

We'll also need to add some Javascript, we'll add this to the scripts folder called MustacheDemo.js and reference it in the Master.cshtml view. 

var page = 1;

function attachToLoadNextPageLink() {
	$(".loadmoreblogposts").on('click', loadNextBlogPage);
}

function loadNextBlogPage() {
	$.get('/umbraco/api/blogpage/page?page=' + (page + 1), function (data) {
		page++;
		var template = $('#blogPageTemplate').html();
		Mustache.parse(template);
		$(".loadmoreblogposts").replaceWith(Mustache.render(template, data));
		attachToLoadNextPageLink();
	});
	return false;
}

attachToLoadNextPageLink();

This hooks up an event to fetch the blog posts from the server via Ajax, parse the Mustache template and use that to render the blog page returned by the server. Now clicking the "load more" link should load additional results each press of the button.

So we're in the position where both the client side and server side code is working but we'll have to maintain 2 versions of the template, a razor one and a Mustache one. If we wanted to modify the markup for the blog posts we'll have to remember to do it in 2 places. This will make the site less maintainable, not such a big problem in this example but a big site might have lots of duplicated templates.

Step 3

So let's now get MVC outputting the blog pages using the same Mustache template as the Javascript. Handily there is a .NET implementation of Mustache called Nustache that has a MVC Nuget package called Nustache.MVC3 that will add a ViewEngine for .NET that will work with Mustache templates. We'll install this using the Package Manager command line:

Install-Package Nustache.Mvc3

This installs the package, but it won't do anything until we add Nustache to the standard .NET ViewEngines. To do this we'll need to instruct MVC to add the new ViewEngine when the application starts. So let's create an App_Start folder and add a new ViewEngineConfig.cs class to it.

using System.Web.Mvc;
using Nustache.Mvc;

namespace MustacheDemo
{
	public class ViewEngineConfig
	{
		public static void RegisterViewEngines(ViewEngineCollection engines)
		{
			engines.Add(new NustacheViewEngine
			{
				// Comment out this line to require Model in front of all your expressions.
				// This makes it easier to share templates between the client and server.
				// But it also means that ViewData/ViewBag is inaccessible.
				RootContext = NustacheViewEngineRootContext.Model
			});
		}
	}
}

The site won't automatically pick this up so we'll also need to hook into the ApplicationStarting event in Umbraco. This requires another class in the App_Start folder which we'll call UmbracoStartup.cs. By inheriting from the Umbraco class ApplicationEventHandler we can hook into Umbraco events. In this case we'll override the ApplicationStarting event and add code to register our new ViewEngine.

using System.Web.Mvc;
using Umbraco.Core;

namespace MustacheDemo
{
	public class UmbracoStartup : ApplicationEventHandler
	{
		protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
		{
			ViewEngineConfig.RegisterViewEngines(ViewEngines.Engines);
		}
	}
}

Because we added this ViewEngine, MVC will now start searching for Mustache templates with a .mustache extension as well as the standard Razor templates it already supports. To share this Mustache template between the client and server by removing the one in Blog.cshtml and making the server write the same one in its place. We'll created a small HtmlHelper extension method that will output the Mustache view file into a razor view to make this easy.

using System.IO;
using System.Web;
using System.Web.Mvc;

namespace MustacheDemo.Helpers
{
	/// <summary>
	/// Load a view from file and output it directly into the page
	/// </summary>
	public static class ViewExtensions
	{
		public static IHtmlString RenderRawContent(this HtmlHelper helper, string serverPath)
		{
			var filePath = HttpContext.Current.Server.MapPath(serverPath);

			var streamReader = File.OpenText(filePath);
			var markup = streamReader.ReadToEnd();
			streamReader.Close();

			return new HtmlString(markup);
		}
	}
}

 To use this we'll need to add a reference to this class' namespace in the namespaces section of the views/web.config.

<system.web.webPages.razor>
  ...
  <pages ...>
    <namespaces>
      ...
      <add namespace="MustacheDemo.Helpers"/>
    </namespaces>
  </pages>
</system.web.webPages.razor>

Now we can copy the current Mustache template that's inside the blog.cshtml to it's own view file at views/shared/blogpage.mustache. This is one of the locations that the ViewEngine will search when looking for views. We can then delete the razor version of the view (BlogPage.cshtml) we created earlier and it will now use the new Mustache version instead.

Finally we'll update the blog.cshtml template to render the Mustache template inline instead of hardcoding it directly into the page by using the new HtmlHelper extension. To do this we'll update the blog.cshtml template to look like this:

@inherits UmbracoTemplatePage<ContentModels.Blog>
@using MustacheDemo.Services
@using ContentModels = Umbraco.Web.PublishedContentModels;
@{
	Layout = "Master.cshtml";
	var blogModel = new BlogPageService().Page(Model.Content);
}
@Html.Partial("~/Views/Partials/SectionHeader.cshtml")
<section class="section">
	<div class="container">
		<div class="blogposts">
			@Html.Partial("BlogPage", blogModel)
		</div>
	</div>
</section>
<script id="blogPageTemplate" type="x-tmpl-mustache">
	@Html.RenderRawContent("~/views/shared/blogpage.mustache")
</script>

We should now have a working blog section which is using a single Mustache template on both the client and server. We can now maintain just the one set of markup in the Mustache template without any chance of it getting out of sync between client side and server side implementations.

Steve Temple

Steve is on Twitter as