Click here to Skip to main content
15,881,559 members
Articles / Web Development / HTML

Client-Side Template in ASP.NET (Web Form / MVC) with Mustache.js

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
2 Sep 2014CPOL7 min read 36.7K   672   20   6
This article will introduce how to use a client-side template in ASP.NET (Web Form / MVC) with Mustache.js

Introduction

The string concatenation approach is an effective way to generate client-side markup for small sections of markup. As the amount of markup grows, it adds complexity to the code that needs to concatenate it, resulting in something that is increasingly difficult to maintain.

Client-side templates are powerful alternatives to simple string concatenation that lets you quickly and effectively transform JSON data into HTML in a very maintainable way. Client-side templates define reusable sections of markup by combining simple HTML with data expressions that can range from a simple placeholder to be replaced with data values, to full-blown JavaScript logic that can perform even more powerful data processing directly within the template.

Mustache (Template System)

Mustache is an open source web template system developed for languages such as ActionScript, C++, Clojure, CoffeeScript, ColdFusion, D, Erlang, Fantom, Go, Java, JavaScript, Lua, .NET, Objective-C, Perl, PHP, Python, Ruby, Scala and XQuery.

You can grab a copy of the library by visiting http://mustache.github.io/. Mustache is “logic-less” template syntax. “Logic-less” means that it doesn’t rely on procedural statements (if, else, for, and so on.): Mustache templates are entirely defined with tags. It is named "Mustache" because of heavy use of curly braces that resemble a mustache. Mustache is used mainly for mobile and web applications.

Mustache.js with ASP.NET Web Form

This section of the article will introduce how to use a client-side template on your web form instead of string concatenation. So let’s start. First, create a web form that will use string concatenation to display person detail. Create a Person class that will be used to show a list of people on the web.

C#
namespace MustacheTemplate
{
    public class Person
    {
        public int PersonID { get; set; }
        public string Name { get; set; }
        public bool Registered { get; set; }
    }
}

Define a web method that will return a list of people from the code behind page of the web form.

C#
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.Web.Services;
 
namespace MustacheTemplate
{
    public partial class ShowTemplate : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
 
        }
        [WebMethod]
        public static string GetAllPerson()
        {
            var RegisteredUsers = new List<Person>();
            RegisteredUsers.Add(new Person() { PersonID = 1, Name = "Sandeep Singh", 
            Registered = true });
            RegisteredUsers.Add(new Person() { PersonID = 2, Name = "Raviender Singh", 
            Registered = false });
            RegisteredUsers.Add(new Person() { PersonID = 3, Name = "Hameer Singh", 
            Registered = true });
            RegisteredUsers.Add(new Person() { PersonID = 4, Name = "Kuldepp Singh", 
            Registered = false });
            JavaScriptSerializer js = new JavaScriptSerializer();
            return js.Serialize(RegisteredUsers);            
        }
    }
}

Now define the web form that renders the person list on the web. The following code snippet is a web form.

HTML
<%@ Page Language="C#" AutoEventWireup="true" 
CodeBehind="ShowTemplate.aspx.cs" Inherits="MustacheTemplate.ShowTemplate" %>
<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="Scripts/jquery-2.1.0.min.js"></script>    
    <script type="text/javascript">
        $(document).ready(function ()
        {
            var tablePerson = $("#tblPerson");
            var divPerson = $("#divPerson");
            $.ajax({
                cache: false,
                type: "POST",
                url: "ShowTemplate.aspx/GetAllPerson",
                data: {},
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (data)
                {
                    var personList = JSON.parse(data.d);
                    $.each(personList, function (id, person)
                    {
                        var personRow = "<tr><td>" + person.PersonID + "</td>" +
                            "<td>" + person.Name + "</td>" +
                            "<td>" + person.Registered + "</td></tr>";
                        tablePerson.append(personRow);
                    });
                },
                error: function (xhr, ajaxOptions, thrownError)
                {
                    alert('Failed to retrieve person list.');
                }
            });
        });
    </script>    
</head>
<body>
    <form id="form1" runat="server">
    <table id="tblPerson" border="1" style="border-collapse:collapse">
        <tr>            
            <th>Id</th>
            <th>Name</th>
            <th>Registered</th>
        </tr>
    </table>
    </form>
</body>
</html>

Now, run the application and you will get the result as shown in Figure 1.1.

Person list

Figure 1.1 List of Persons

Note that from the web form code, you are concatenating a string for tags to show person details. It is small markup section so it’s not looking complex but when you develop a complex markup, then it will not be easy to maintain and read so you need to define a separate template for it and call it where it is needed on the web form. So let’s see the code for the template.

The following code snippet is for a Mustache template that will show a person's detail.

HTML
<script type="text/template" id="tempPerson">        
    <tr>
       <td>{{PersonID}}</td>
       <td> {{Name}}</td>
       <td>{{Registered}}</td>
    </tr>        
</script>

The following code snippet renders the Mustache template above in JavaScript.

HTML
function (id, person) 
{
  var template = $('#tempPerson').html();
  var html = Mustache.render(template, person);
}

The following code snippet is for the web form that uses the Mustache template and renders it on the web. Your Person class and get Person list method will be the same as the pervious.

HTML
<%@ Page Language="C#" AutoEventWireup="true" 
CodeBehind="MustacheExample.aspx.cs" 
Inherits="MustacheTemplate.MustacheExample" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <script src="Scripts/jquery-2.1.0.min.js"></script>
    <script src="Scripts/mustache.js"></script>    
    <script type="text/javascript">
        $(document).ready(function ()
        {
            var tablePerson = $("#tblPerson");
            var divPerson = $("#divPerson");
            $.ajax({
                cache: false,
                type: "POST",
                url: "ShowTemplate.aspx/GetAllPerson",
                data: {},
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (data)
                {
                    var personList = JSON.parse(data.d);
                    $.each(personList, function (id, person)
                    {
                        var template = $('#tempPerson').html();
                        var html = Mustache.render(template, person);
                        tablePerson.append(html);
                    });
                },
                error: function (xhr, ajaxOptions, thrownError)
                {
                    alert('Failed to retrieve person list.');
                }
            });
        });
    </script>    
    <script type="text/template" id="Script1">        
        <tr>
            <td>{{PersonID}}</td>
            <td> {{Name}}</td>
            <td>{{Registered}}</td>
        </tr>        
    </script>
 
</head>
<body>
    <form id="form2" runat="server">
    <table id="Table1" border="1" style="border-collapse:collapse">
        <tr>            
            <th>Id</th>
            <th>Name</th>
            <th>Registered</th>
        </tr>
    </table>
    </form>
</body>
</html>

Let’s run the application and you will get the same result as in Figure 1.1.

Now you want to show only registered users; the registered users that have the registered property as true. You then need to change your template as in the following:

HTML
 <script type="text/template" id=" tempPerson">
    {{#Registered}}
     <tr>
        <td>{{PersonID}}</td>
        <td> {{Name}}</td>
        <td>{{Registered}}</td>
     </tr>
    {{/Registered}}
</script>

The code above returns only the two person's details that have the property Registered as true. You add your condition to the template without if and else; that is why it’s called “Logic-Less”.

Mustache.js with ASP.NET MVC

In the previous article, “Rendering a Partial View and JSON Data Using AJAX in ASP.Net MVC”, I introduced you to the concept of rendering JSON data in ASP.NET MVC where I used the string concatenation approach but this section of the article will introduce you to how to use a Mustache client template in an ASP.NET MVC application instead of string concatenation. I will explain this concept with a simple example. The example is that books are showing in a web depending on publisher. I choose a publisher from a dropdown list then the books information is shown in the web page depending on publisher. So let’s see this example in detail.

Getting Started

I add the ADO.NET Entity Model to the application to do the database operations mapped from the “Development” database. The ADO.NET Entity Model is an Object Relational Mapping (ORM) that creates a higher abstract object model over ADO.NET components. This ADO.NET Entity Model is mapped to with “Development” database so the context class is “DevelopmentEntities” that inherits the DbContext class.

This “Development” database has two tables, one is the Publisher table and the other is the BOOK table. Tables have 1-to-many relationships, in other words one publisher can publish multiple books but each book is associated with one publisher. If you want to learn more about this application database design, then please check my previous article “An MVC Application with LINQ to SQL”.

The ADO.NET Entity Model is mapped to both tables. The connection string for this has the same name as the context class name and this connection string is created in the web.config file. You can change the name of the connection string. The context class name and connection string name is just a convention, not a configuration, so you can change it with a meaningful name. The following Figure 1.2 shows the ADO.NET Entity Model mapping with both tables.

 The ADO.NET Entity Model mapping with Publisher and Book tables

Figure 1.2 The ADO.NET Entity Model mapping with Publisher and Book tables

Now the ADO.NET Entity Model is ready for the application and it's time to move on the next step of the application, the model design so let’s see the application’s model.

Model Design

As you already know, the purpose of the application is to create two entities (Publisher and BOOK) that I have already created as the same, therefore I need to create two models, one for the publisher and the other for the books. I create a publisher model (Publisher.cs) under the Model folder that has a publisher id and publisher list as in the following code snippet.

C#
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
 
namespace MustacheMVCApplication.Models
{
    public class PublisherModel
    {
        public PublisherModel()
        {
            PublisherList = new List<SelectListItem>();
        }
 
        [Display(Name = "Publisher")]
        public int Id { get; set; }
        public IEnumerable<SelectListItem> PublisherList { get; set; }
    }
}

After the publisher model, I create a book model (Book.cs) in the same folder that has basic properties related to a book. The following is a code snippet for the book model.

C#
namespace MustacheMVCApplication.Models
{
    public class BookModel
    {
        public string Title { get; set; }
        public string Author { get; set; }
        public string Year { get; set; }
        public decimal Price { get; set; }
    }
}

Now the models are ready for use so now to move on to the controller.

Controller Design

I create two controllers, one for the publisher that shows a publisher’s list in a dropdown list and another is a book controller that shows book details depending on publisher. The publisher controller defines a single action method that is the same in both concepts; rendering a partial view and JSON data and the book controller also defines an action method, that renders JSON data.

Now create a publisher controller (PublisherController.cs) under the Controllers folder as per the MVC convention. This control has a single action method to show the publisher list on the view. The following is a code snippet for the publisher controller.

C#
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MustacheMVCApplication.Models;
 
namespace MustacheMVCApplication.Controllers
{
    public class PublisherController : Controller
    {
        public ActionResult Index()
        {
            PublisherModel model = new PublisherModel();
            using (DAL.DevelopmentEntities context = new DAL.DevelopmentEntities())
            {
                List<DAL.Publisher> PublisherList = context.Publishers.ToList();
                model.PublisherList = PublisherList.Select(x =>
                                        new SelectListItem()
                                        {
                                            Text = x.Name,
                                            Value = x.Id.ToString()
                                        });
                                 }
            return View(model);
        }
    }
}

Now create book a controller (BookController.cs) under the Controllers folder of the application. ASP.NET MVC offers native JSON support in the form of the JsonResult action result, that accepts a model object that it serialized into the JSON format. In order to add AJAX support to your controller actions via JSON, simply use the Controller.Json() method to create a new JsonResult containing the object to be serialized.

Now create an action method BooksByPublisherId() in the book controller that returns JsonResult. This action method retrieves a list of books depending on publisher id that passes as a parameter in this action method. The following is a code snippet for this book controller.

C#
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MustacheMVCApplication.Models;
 
namespace MustacheMVCApplication.Controllers
{
    public class BookController : Controller
    {
        public JsonResult BooksByPublisherId(int id)
        {
            IEnumerable<BookModel> modelList = new List<BookModel>();
            using (DAL.DevelopmentEntities context = new DAL.DevelopmentEntities())
            {
                var books = context.BOOKs.Where(x => x.PublisherId == id).ToList();
                modelList = books.Select(x =>
                                            new BookModel()
                                            {
                                                Title = x.Title,
                                                Author = x.Auther,
                                                Year = x.Year,
                                                Price = x.Price
                                            });
            }
            return Json(modelList,JsonRequestBehavior.AllowGet); 
        } 
    }
}

I define a route for this action in the RegisterRoute() method under the RouteConfig class (App_Start/RouteConfig.cs).

C#
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Publisher", action = "Index", id = UrlParameter.Optional }
    );
    routes.MapRoute("BooksByPublisherId",
    "book/booksbypublisherid/",
    new { controller = "Book", action = "BooksByPublisherId" },
    new[] { "MustacheMVCApplication.Controllers" });
}

Now create an Index view for the publisher with a dropdown list for the publisher and that shows the book details depending on the value selected in the publisher dropdown list using AJAX. This view uses a Mustache client-side template that shows book details. The following is a code snippet for a book detail.

HTML
<script type="text/template" id="tempBook">
   <b>Title :</b> {{Title}} <br/>
   <b> Author :</b> {{Author}}<br/>
   <b> Year :</b> {{Year }} <br/>
   <b> Price :</b> {{Price }}<hr/>
</script>

The following code snippet is for rendering this template in JavaScript.

HTML
function (id, book)
{
    var template = $('#tempBook').html();
    var bookData = Mustache.render(template, book);
} 

The following is a code snippet for the entire Index view.

HTML
@model MustacheMVCApplication.Models.PublisherModel
 
<script src="~/Scripts/jquery-2.1.0.min.js"></script>
<script src="~/Scripts/mustache.js"></script>    
<script type="text/javascript">
    $(document).ready(function () {
        $("#Id").change(function () {
            var id = $("#Id").val();
            var booksDiv = $("#booksDiv");
            $.ajax({
                cache: false,
                type: "GET",
                url: "@(Url.RouteUrl("BooksByPublisherId"))",
                data: { "id": id },
                success: function (data) {
                    var result = "";
                    booksDiv.html('');
                    $.each(data, function (id, book) {
                        var template = $('#tempBook').html();
                        var bookData = Mustache.render(template, book);
                        booksDiv.append(bookData);
                    });
                    
                },
                error: function (xhr, AJAXOptions, thrownError) {
                    alert('Failed to retrieve books.');
                }
            });
        });
    });
</script>
<script type="text/template" id="Script2">        
        <b>Title :</b> {{Title}} <br/>   
        <b> Author :</b> {{Author}}<br/> 
        <b> Year :</b>  {{Year }} <br/> 
        <b> Price :</b> {{Price }}<hr/>  
    </script>
<div>
    @Html.LabelFor(model=>model.Id)
    @Html.DropDownListFor(model => model.Id, Model.PublisherList)
</div>
<div id="booksDiv"> 
</div>

Let’s run the application. You will get results as in Figure 1.3.

 Books detail of a publisher

Figure 1.3 Books detail of a publisher

Conclusion

While the client template approach may seem like a lot of work, in most cases, the ease of maintenance and the lower bandwidth costs that it allows make it well worth the up-front cost. When your application relies on many AJAX interactions that result in complex client-side markup, a client template is often a great choice. If you have any doubts, then please comment here or directly connect with me using https://twitter.com/ss_shekhawat.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
India India
He is awarded for Microsoft TechNet Guru, CodeProject MVP and C# Corner MVP. http://l-knowtech.com/

Comments and Discussions

 
GeneralMy vote of 5 Pin
Lilanga2-Sep-14 20:40
professionalLilanga2-Sep-14 20:40 
GeneralRe: My vote of 5 Pin
Sandeep Singh Shekhawat3-Sep-14 18:12
professionalSandeep Singh Shekhawat3-Sep-14 18:12 
QuestionComparison to jQuery tmpl Pin
MarkusVT8822-May-14 20:33
professionalMarkusVT8822-May-14 20:33 
AnswerRe: Comparison to jQuery tmpl Pin
Sandeep Singh Shekhawat23-May-14 17:17
professionalSandeep Singh Shekhawat23-May-14 17:17 
QuestionNice into Pin
Jan Hansen22-May-14 1:31
professionalJan Hansen22-May-14 1:31 
Thanks for this writeup. Good to have people like you sharing knowledge Smile | :)

I was wondering though if you didn't misplace the route in your MVC example. AFAIK the book-route will never be used since the default-route will catch almost anything (including your AJAX-call /book/booksbypublisherid/). Or have I missed something...?

Can't Mustache handle a Collection of items, so you don't have to iterate over the items returned in JavaScript (at least you briefly touched on this in your webforms example with the list of Registeded persons)...?
AnswerRe: Nice into Pin
Sandeep Singh Shekhawat23-May-14 17:14
professionalSandeep Singh Shekhawat23-May-14 17:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.