Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Integrating Knockout, JSRender, and Twitter bootstrap with MVC4

0.00/5 (No votes)
12 Jul 2012 1  
How to use JSRender templates with knockout.js in MVC4

Introduction

MVC framework definitely changed the way we write our web applications now with clear separations between presentation layer, data and logic.

With HTML5/CSS3 and jQuery we can see significant shift to client site implementations.

New libraries like JSRender and Knockout brought even more separations of concerns to client site code.

In this short article I would like to show an example of how I implemented integration between MVC4, JSRender, and Knockout.

Background

This code is mostly designed to show client site technics for decupling your data from view. I’ll only use controllers to simulate data retrieval from repositories.

Prepare your solution

  1. Create a new MVC 4 application
  2. Use NuGet to add the following packages:
    1. jQuery
    2. Knockout
    3. Autofac
    4. Tweeter bootstrap
  3. Download Jsrender from https://github.com/BorisMoore/jsrender/

Add references to new dependencies 

In the web project under App_Start use BundleConfig.cs to register bootstrap files:

bundles.Add(new StyleBundle("~/Content/bootstrap/css"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap/js").Include("~/Scripts/bootstrap.js"));
bundles.Add(new ScriptBundle("~/bundles/custom").Include("~/Scripts/knockout-2.1.0.js",	"~/Scripts/jsRender.js"));
bundles.Add(new ScriptBundle("~/bundles/demo").Include("~/Scripts/demo.js"));  

Register those bundles in _Layout.cshtml

@Styles.Render("~/Content/themes/base/css",
"~/Content/css", "~/Content/bootstrap/css") @Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/custom")
….
@Scripts.Render("~/bundles/bootstrap/js")

JsRender 

JsRender is a new replacement for jQuery templates.

More samples from Boris Moore: here.

The advantage of JSRender that it is much faster the jQuery template and provides clear separation of concerns.

In this example I’m using external file templates for even better separation. JSRender templates can live on the same page in <script id='myTemplate' type='text/x-jsrender></script> tag.

The advantage of using templates in the files is that the templates can be reused on different pages much easier.

The question was who JSRender finds templates to render.

In my example I’ve a demo.js file with two functions: .getPath and .renderTemplate:  which will look forTemplates/HTML/ folder.

getPath function is using naming conventions to resolve the file name and renderTemplate function is loading the file “_” + name +”.tmpl.html”.

The jQuery .get () function will read the file and load context into the memory and then render the template and bind data with Knockout.js.

Knockout.js is used to provide MVVM pattern and trigger binding and refresh the template.  

Creating JSRender templates 

First I’ll add a template without any data binding.  

I the web project add new folder Templates/HTML

Add new html files:

  • _home.tmpl.html
  • _head.tmpl.html
  • _contacts.tmpl.html

Remove all content from new file.

_header.tmpl.html content:

<p>
To learn
more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
Bla &hellip; bla&hellip; bla 
</p>

_home.tmpl.html content:

<h3>We suggest the following:</h3>
<ol class="round">
    <li class="one">
        <h5>Getting Started</h5>
        Bla &hellip; bla&hellip; bla
</ol>

Bind JSRender templates to view 

Now we will bind simple jsrender templates from external file to load into Index.cshtml view.

In order display template on the Index.cshtml view page we need to add custom script to bind with knockout library.

We will use two different DIVs for data binding: one for header and one for body:

Index.cshtml should looks like this:

@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>            
        </div>
    </section>
}
@Scripts.Render("~/bundles/demo")
<div data-bind="html: demo.head.headHtml" id="headHtml"></div>
<div data-bind="html: demo.body.bodyHtml"></div>

Please not the use of name space in this case – demo.

Create custom script

I will use custom script file we created earlier to specify template we want to load. This is generic function and will use asynchronous process to render template after it will done reading template file.

We add the following code: 

var demo = demo || {};
 
$(function() {
    demo.utils = function () {
        var getPath = function (name) {
            return "../Templates/HTML/_" + name + ".tmpl.html";
        };
 
        var renderTemplate = function (item) {
            var file = getPath(item.name);
            $.when($.get(file))               
		.done(function (tmplData) {
                    $.templates({ tmpl: tmplData });                   
		    item.selector($.render.tmpl(item.data));                    
                });
        };
        return {
            getPath: getPath,           
	    renderTemplate: renderTemplate
        };
    }();
});

I am using reviling patter where function will call itself (Note () at the end of function).

Time to declare our UI view model for home page, this can be done in separate file, but simplicity I’ll just reuse our demo.js file

Now we can add our model to demo.js file

 $(function() {
    demo.head = function() {
        var headHtml = ko.observable(),
            loadTemplate = demo.utils.renderTemplate({
                name: "head",
                data: "",
                selector: headHtml
            });

        return {
            loadTemplate: loadTemplate,
            headHtml: headHtml
        };
    }();

    demo.body = function() {
        var bodyHtml = ko.observable(),
            loadTemplate = demo.utils.renderTemplate({
                name: "home",
                data: "",
                selector: bodyHtml
            });

        return {
            bodyHtml: bodyHtml,
            loadTemplate: loadTemplate
        };
    }();

demo.contacts = function() {
        var 
            contactHtml = ko.observable(),
            loadContacts = function() {
                $.ajax({
                    url: '/Home/LoadContacts',
                    type: "POST",
                    /*contentType: "application/json; charset=utf-8",*/
                    dataType: 'json',
                    success: function(d) {                        
                        demo.utils.renderTemplate({
                            path: "../",
                            name: 'contacts',
                            data: d,
                            selector: contactHtml
                        });
                    }
                });
            };

        return {           
            contactHtml: contactHtml,
            loadContacts: loadContacts
        };
    }();    
    
ko.applyBindings(demo.head);
ko.applyBindings(demo.body);

demo.contacts.loadContacts(); // Get data before binding template
ko.applyBindings(demo.contacts);
    
});

At this point if we run the app we should see not difference between original MVC home page and our customized one. 

Now we want to add JSRender view which will bind real data and that data will come from the server side. We will retrieve the data via AJAX call. 

Open Contacts.cshtml and modify it. It should looks like this:

@{
    ViewBag.Title = "Contacts";
}
<hgroup class="title">
    <h1>@ViewBag.Title.</h1>
    <h2>@ViewBag.Message</h2>
</hgroup>

@Scripts.Render("~/bundles/demo")
<div data-bind="html: demo.contacts.contactHtml" id="headHtml"></div>

In order to load data from controller we’ll add a new AJAX call.

Open HomeController.cs and add the following code:

[HttpGet]
public JsonResult LoadContacts()
{
	var contacts = new List<Contact>
					   {
						  new Contact {fn = "Ben", ln = "Doe", title = "Developer", 
						               email = "myemail@code.com", phone = "888-555-6565"},
						  new Contact {fn = "John", ln = "Smith", title = "Boss", 
						               email = "boss@code.com", phone = "888-555-7777"}
					   };
	return Json(contacts);
}

Also you’ll need to create a simple Contact class:

public class Contact
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public string Title { get; set; }
	public string Phone { get; set; }
	public string Email { get; set; }
}

In our demo.js file we need to modify template binding and load template after we’ve received the data from AJAX call. 

Link to latest version of the source code project: here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here