This article introduces a different concept of the view engine - a client-side view engine that renders a view in a client browser. In this concept, the model and the controller are still on the server-side. However, instead of the HTML, JSON is generated as an output from the server-side, it is accepted on the client-side, and HTML is generated using the JavaScript templating engine.
Table of Contents
- Introduction
- Background
- Using the Code
- Model
- Controller
- View
- Implementation
- List Page
- Details Page
- Edit Page
- Hierarchical View
- Conclusion
- History
Introduction
Microsoft ASP.NET MVC framework follows a standard MVC pattern - the Model contains data that will be shown, the Controller performs actions when some event happens, initializes the Model, and passes it to the View, and the View takes a Model and renders the HTML output that would be sent to the client browser. This architecture is shown in the following figure:
The client (browser) sends an HTTP web request to the server-side. On the server, we have controllers that handle the request, takes data using the model, and passes it to the view. The view generates HTML that should be sent back to client. In MVC 3, there are several view engines that can be used - the standard ASP.NET view engine, Razor, Spark, NHaml, etc. All of these view engines use different syntax for generating the view; however, they all work the same way - the model is taken on the server-side, formatted using the template, and HTML is sent to the client browser.
This article introduces a different concept of the view engine - a client-side view engine that renders a view in a client browser. In this concept, the model and the controller are still on the server-side. However, instead of the HTML, JSON is generated as an output from the server-side, it is accepted on the client-side, and HTML is generated using the JavaScript templating engine. This architecture is shown in the following figure:
Sending an HTTP request to the controller and handling the model is the same as in the standard method. However, instead of passing the model object directly to the server-side view, in this case, the model is sent to the client side formatted as a JSON object where it is processed by the client side view engine.
Background
This example shows how the client-side JavaScript template engine in the ASP.NET MVC project can be used instead of the standard server-side templating engines. JavaScript templating engines use JSON objects as a model and generate HTML on the client side. The principle of the JavaScript template engine is shown in the following figure:
With JavaScript template engines, you will need to pass the model object (usually some JavaScript object) to the view. The view will have a template that defines how this model object will be rendered. Once it binds the JavaScript model with the template, it will produce HTML as a standard server-side view engine. As you can see, client-side templating engines work the same way as standard server-side MVC rendering engines. The only difference is that the views are processed in the browser.
Currently, there are several templating engines such as jsRender (successor of the deprecated jQuery template), Knockout, Pure, noTemplate, jsTemplate, Populate, and jTemplates, but this example uses the jQuery loadJSON templating engine. The jQuery loadJSON plug-in is a templating engine that binds a JSON object to a clean HTML template and generates the view on the client-side. To bind the JavaScript model with a template, you will need to call something like the following line of code:
$("#template").loadJSON(model);
$("#template").render(model);
This call will take the HTML fragment from the HTML in the current browser, and use it as a template for binding with the JavaScript object (model). As an example, imagine that you have the following JSON object that should be placed in the template:
model = {
"Name":"Emkay Entertainments",
"Address":["Nobel House","London","UK"],
"Contact":"Phone"
}
| <div id="template">
<textarea id="Name" />
<span class="Address"></span>
<input name="Contact" />
</div>
|
If you call loadJSON
with this model applied to the template, you will get the following output:
<div id="template">
<textarea id="Name">Emkay Entertainments</textarea>
<span class="Address">Nobel House</span>
<span class="Address">London</span>
<span class="Address">UK</span>
<input name="Contact" value="Phone"/>
</div>
This is a simple JavaScript templating engine that has all the necessary features to implement an effective client-side templating engine, however you have other ones too. As an example, the same code generated using the jsrender template would be:
var model = {
"Name":"Emkay Entertainments",
"Address":[{"Item":"Nobel House"},
{"Item":"London"},
{"Item":"UK"} ],
"Contact":"Phone"
}
| <script id="template" type="text/x-jsrender">
<textarea id="Name">{{:Name}}</textarea>
{{for Address}}
<span>{{:Item}}</span>
{{/for}}
<input name="Contact" value="{{:Contact}}"/>
</script>
|
In the JavaScript, you can see a very similar data structure. In the template are added placeholders that match the names of the properties surrounded with {{:
and }}
. Also, here you have the ability to use for
loops. In order to load a model to the template, you will need to use something like the following code:
$( "#view" ).html(
$( "#template" ).render( model )
);
As a result, an element with ID "view
" will be populated with the HTML that is a result of the merging model and the template. The result will be like the one shown in the following example:
<div id="view">
<textarea id="Name">Emkay Entertainments</textarea>
<span>Nobel House</span>
<span>London</span>
<span>UK</span>
<input name="Contact" value="Phone"/>
</div>
Although the loadJSON
plug-in will be used in the examples, alternative implementation examples with Pure and jQuery template are shortly described in some sections.
Why are we using this architecture? There are a few benefits that come with this kind of architecture:
- Performances - In this architecture, view processing is moved to the browser, where are used resources of the browser. The server should return responses faster because there is no view processing on the server-side.
- Throughput - In this architecture, instead of the HTML being returned, a JSON object is returned which is smaller than HTML because it does not contain unnecessary tags that wrap pure information. JSON objects contain only data that should be shown and they are loaded faster.
- Caching - HTML templates that are used on the client side do not contain any data. Therefore, they can be cached and reused for different view pages. The only thing that changes is a JSON data that is loaded in the template.
- Cross platform support - In this architecture, the same web server can support different platforms, from mobile devices to standard browsers, without knowing who is the client. The server returns the same JSON data to all clients, and you can put different templates on the client side that will be the best match for the device resolution and supported features. As an example, on a mobile device, you can define a smaller template where will not be shown rich UI elements, while in a standard browser, you will use a full rich-client UI.
In the following sections, I will show you how to create a JSON based ASP.NET MVC application using client-site templating engines.
Using the Code
In this article, a code example is presented which demonstrates how a client-side view engine can be used in ASP.NET MVC. The following sections show how the jQuery loadJSON plug-in can be integrated into an ASP.NET MVC application in order to replace the standard server-side view engines.
Model
Two classes that contain information about companies and their employees are used as a model in this example. The source code of the Company
/Employee
model classes is given in the following listing:
public class Company
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Town { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public string Contact { get; set; }
public bool IsFeatured { get; set; }
public string[] Country { get; set; }
public string Logo { get; set; }
public Employee Manager { get; set; }
public Employee[] Employees { get; set; }
}
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
The Company
class has two references to the Employee
class, one to the single employee representing the manager and the array of employees for this company.
These model classes will be serialized in the controller and the JSON representation of the instances of the Company
/Employee
classes will be provided to the view that is placed on the client-side.
Controller
The controller is not a standard controller that reacts on user action and returns the view as it works in standard server-side view engines. In the JavaScript templating engines, the controller should return just a JSON object that will be used as the model on the client-side. This JSON object will be accepted in the JavaScript templating function and the HTML output will be generated directly in the browser page. This example contains three pages: list of companies, details about a company, and a form where company details can be edited. Hence, there are two action methods that should be implemented in the controller. The first action returns a list of companies, and the second one returns a single company by ID (this action will be called when details are shown and when a form with company data is populated). The source code for the controller is given in the following listing:
public class CompanyController : Controller
{
public ActionResult List()
{
return Json(DataRepository.GetCompanies(),
JsonRequestBehavior.AllowGet);
}
public ActionResult Data(int? ID)
{
return Json(DataRepository.GetCompanies().First(
c => c.ID == ID), JsonRequestBehavior.AllowGet);
}
}
The Action
methods take the model classes, format them as JSON results, and return the JSON to the view engine. The view engine on the client-side will call these actions when the model is required by the view.
View
When a JavaScript templating engine is used, the view can be a static page. You can use standard .aspx pages as views but a plain HTML page can be used as well. The given example has four view pages:
- The list page where the list of companies is shown
- The details page where a single company's details are shown
- An edit page where information about a single company is loaded into a form where the details can be changed and posted to the server-side (although posting the results is out of the scope of this article).
- A hierarchy page where how the client-side template can be used to render complex nested structures is shown - this is the only example where the
Employee
class will be used.
These are typical cases of usage of the client pages. In the following sections, these view pages are described in detail and how the client-side engines can be used to render them is explained.
Implementation
In this section, I will show how you can implement standard list, details, and edit pages with jQuery templates.
List Page
The list page is used to define a template that will be used to list the companies in the browser. A static view page is used in this example as shown in the following listing:
<html>
<head>
<title>Listing JSON aray using the HTML template</title>
<link rel="stylesheet" type="text/css" href="/Content/style.css" />
<script src="/scripts/jQuery-1.4.4.min.js")" type="text/javascript"></script>
<script src="/scripts/jQuery.loadJSON.js")" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$('li').loadJSON('/Company/List');
});
</script>
</head>
<body>
<div id="page-wrap">
<div id="contact-area">
<ul>
<li><a href="details.html" class="ID">
<span class="Name"></span>
</a><span class="Address"></span>
</li>
</ul>
</div>
</div>
</body>
</html>
There is a plain HTML code placed in the unordered list that defines the item template that will be used to generate output. This part of the code is shown in the following listing:
<li>
<a href="details.html" class="ID">
<span class="Name"></span>
</a>
<span class="Address"></span>
</li>
The li
tag contains HTML elements whose class
attributes match the properties in the model class. The view is initialized using this JavaScript line from the heading:
$('li').loadJSON('/Company/List');
This line loads a JSON array from the /Company/List URL and loads it into the lI
element. The Company/List URL activates the List
method in the Company controller which returns a list of companies formatted as a JSON array. When the loadJSON
plug-in gets applied on this element, properties of the objects are placed in the template. Moreover, this template item is replicated for each instance of the model in the array that is bound to the template. As a result, a set of li
elements will be created representing the list of companies that are loaded from the /Company/List URL. Shown below is the generated view in the browser:
The jQuery loadJSON
plug-in checks the type of HTML elements before it loads values in them. In most elements, the value will be placed as an inner text of the element (e.g., SPAN
, P
, or DIV
tags). However, if it finds a link element, instead of inner text, the plug-in adds a property as an additional parameter in the URL placed in the href
attribute. In the example above, the parameters ID=17, ID=18, ID=19, etc., are appended to the href
attribute of the link. Therefore, to add a text of the link, you will need to place an additional SPAN
tag inside the link that will be loaded from the different property. You can find a live example of the list functionality here.
Alternative Implementation using the jQuery Template/jsRender Template
Note that you can use any other template engine if you want. As an example, if a jQuery template is used, the template would be something like the code shown in the following listing:
<ul id="template">
<li>
<a href="/Home/PopulateEdit/${ID}">${Name}</a>
${Address}
</li>
</ul>
In jQuery template, placeholders are names of properties enclosed in ${
and }
- in the example above, the ID
, Name
, and Address
properties of the JSON objects will be placed in the ${ID}
, ${Name}
, and ${Address}
placeholders. The JavaScript code that should be used to load JSON data in the template is shown below:
$.template("my_template", $("#template").html());
$.getJSON('/Home/Populate', function (data) {
$("#template").html($.tmpl("my_template", data));
});
In the jQuery template, you need to take the template code placed in the unordered list with an id
"template
", and compile it using a $.template()
method. The compiled template can be referenced by the name my_template
when the view is generated. Then, an AJAX call is executed to load JSON from the /Populate/List URL and data returned by this controller is bound to the template and set as the HTML code of the unordered list with an id
"template
". The result of the template is the same as the result returned by the loadJSON plug-in - there is only a different syntax for the view and initialization code.
The jQuery template plug-in is now deprecated, no longer in active development or maintenance. I would recommend to use jsRender instead. It has very similar sintax with minor changes. As an example, in the jsRender, instead of the ${ID}
you are using:
{{:ID}}
if you want to put raw value of data, or {{>ID}}
if you want to use HTML encoding
Equivalent code that loads HTML from the template using the data returned via AJAX is shown in the following example:
//Perform AJAX call and pass returned JSON array to template
$.getJSON('/Home/Populate', function (data) {
$("#view").html($("template").render(data));
});
As you can see, it is very similar, so if you are already using jQuery template plugin and you want to convert your code to other template, I would recommend jsRender.
Details Page
The Details page is used to show the details about a single company. It takes a JSON object from the /Company/Details page and loads the JSON object in the HTML template. The Details page used in this example is shown in the listing below:
<html>
<head>
<title>Displaying details of the single JSON object</title>
<link rel="stylesheet" type="text/css" href="/Content/style.css" />
<script src="/scripts/jQuery-1.4.4.min.js")" type="text/javascript"></script>
<script src="/scripts/jQuery.loadJSON.js")" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
var id = window.location.href.match("(ID=|id=|/)[0-9]+")[0].match("[0-9]+");
$('div#data').loadJSON('/Company/Data/'+id);
});
</script>
</head>
<body>
<div id="page-wrap">
<div id="contact-area">
<div id="data">
<h1 id="Name"></h1>
<img id="Logo" alt="" src=""/>
<label for="Address">Address:</label>
<span id="Address"></span>
<label for="Contact">Contact by:</label>
<span id="Contact"></span>
<label for="Town">Town:</label>
<span id="Town"></span>
<form action="edit.html" method="get">
<input type="hidden" id="ID" name="ID"/>
<input type="submit" value="Edit" class="submit-button"/>
</form>
</div>
</div>
</div>
</body>
</html>
The page contains a definition of blank HTML templates with elements that have id
attributes that match properties from the model. The JavaScript call in the heading takes the ID
parameter from the page URL and calls the /Company/Details server-side page with that parameter, as shown in the following code example:
var id = window.location.href.match("(ID=|id=|/)[0-9]+")[0].match("[0-9]+");
$('div#data').loadJSON('/Company/Data/'+id);
The controller action Details(int? ID)
in CompanyController
will be called when this URL is sent to the server, and a single company will be returned as a JSON result. This JSON object will be loaded in the DIV
with the "data
" ID
and elements in that DIV
will be populated. You can see a live example of the Details page here. An example of the generated view for one company in the sample project is shown in the figure below:
Alternative Implementation using the Pure Templating Engine
Similar to the previous example, instead of loadJSON, any other templating engine can be used. As an example, if the Pure templating engine is used, the template can be the same as the one shown in the code sample (Pure uses the same clean HTML code as loadJSON
). The JavaScript initialization call is shown in the following example:
var id = window.location.href.match("(ID=|id=|/)[0-9]+")[0].match("[0-9]+");
$.getJSON('/Home/Populate/'+id, function (company) {
$("'div#data'").render(company, { '#Name': 'Name', '#Address': 'Address',
'#Logo@alt': 'Name', '#Logo@src':'Logo' });
});
In the example above, an AJAX call that loads the JSON object from the /Home/Populate/<<id>> URL is performed, and in the callback function, the JSON object is loaded into the div
with ID
'data
', as in the previous example. The second parameter of the Render
function is a directive that maps HTML elements to JSON properties. In the directive shown in the example above, the Name
and Address
properties of the JSON object are loaded in the elements with id
s Name
and Address
. In the alt
attribute of the element with id
'Logo
' is loaded the Name
, and in the src
attribute of the element with ID
'Logo
' is placed the Logo
property of the JSON object. The advantage of Pure is that you can define custom mapping rules between elements, their attributes, and properties of the JSON object. If you don't want to use directives, you can use the .autoRender()
function instead of .render()
. The function .autoRender()
is similar to the .loadJSON()
function shown in the example above; however, the .autoRender()
function matches elements with properties by class name only (this is a "default directive" in the Pure templating engine).
Editing Details
The Edit page is similar to the Details page. However, here, a single company record is loaded into the HTML form. An example of the Edit page is shown in the following listing:
<html>
<head>
<title>Load Form with JSON data</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="/Content/style.css" />
<script src="/scripts/jQuery-1.4.4.min.js")" type="text/javascript"></script>
<script src="/scripts/jQuery.loadJSON.js")" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
var id = window.location.href.match("(ID=|id=|/)[0-9]+")[0].match("[0-9]+");
$('form').loadJSON('/Company/Data/' + id);
});
</script>
</head>
<body>
<div id="page-wrap">
<div id="contact-area">
<form name="form_simple" id="form-simple" action="details.html" method="get">
<input type="hidden" id="ID" name="ID" />
<label for="Name">Name</label>
<input name="Name" id="Name" type="text" />
<label for="Address">Address</label>
<textarea name="Address" id="Address" rows="5" cols="20"></textarea>
<label for="Country">Country</label>
<select name="Country" multiple="multiple">
<option value="">-</option>
<option value="UK">United Kingdom</option>
<option value="SRB">Serbia</option>
<option value="USA">United States of America</option>
<option value="FRA">France</option>
</select>
<label for="IsFeatured">Is Featured</label>
<input name="IsFeatured" id="IsFeatured" type="checkbox" value="true"/>
<label for="Town">Town</label>
<select name="Town" id="Town">
<option value="" selected="selected">-</option>
<option value="London">London City</option>
<option value="Liverpool">Liverpool City</option>
<option value="Lothian">Lothian City</option>
<option value="Newcastle">Newcastle City</option>
<option value="Buckinghamshire">Buckinghamshire City</option>
<option value="Essex">Essex City</option>
</select>
<label for="Contact">Contact</label>
<input name="Contact" type="radio" value="Email"/> Email
<input name="Contact" type="radio" value="Phone" /> Phone
<input name="Contact" type="radio" value="Post" /> Post
<input type="submit" value="Details" class="submit-button" />
</form>
</div>
</div>
</body>
</html>
The heading should contain a JavaScript call that loads the JSON object from the URL into the form. This JavaScript call is shown in the following code:
var id = window.location.href.match("(ID=|id=|/)[0-9]+")[0].match("[0-9]+");
$('form').loadJSON('/Company/Data/' + id);
The result of this call is a form populated with properties of the JSON object loaded from the /Company/Data/<ID> URL. A live example of the form populated with the loadJSON
plug-in can be found here and detailed rules that are used while populating the form are described here. An example of the form populated with JSON data is shown in the figure below:
The jQuery loadJSON
plug-in will check the type of each form element where the JSON properties should be loaded, and depending on the type, it will put the property as a text in text boxes and text areas, selected items in radio button groups, single, and multi selection lists, and checked/unchecked state in check boxes. In other plug-ins, you would need to manually set the mapping rules for loading form elements.
In other plug-ins, I have not found an equivalent simple implementation of populating forms, so this example will not be shown in any alternative implementations.
Hierarchical View
The last view shows how the complex/hierarchical structures can be rendered using client-side engines. The previous examples used simple structures (array of companies, single company), but as you might see in the model, there is a relation between Company
and Employees
. When the list of companies is returned by the Company
/List
action, the following structure is returned as a JSON result:
[
{ "ID": 17, "Name": "Emkay Entertainments",
"Address": "Nobel House, Regent Centre",
"Town": "Lothian", "Contact": "Phone", "IsFeatured": true,
"Manager": { "FirstName": "Nick", "LastName": "Donovan" },
"Employees": [ { "FirstName": "John", "LastName": "Doe" },
{ "FirstName": "Alex", "LastName": "Watkins" },
{ "FirstName": "Mick", "LastName": "Henning"}]
},
{ "ID": 18, "Name": "The Empire", "Address": "Milton Keynes Leisure Plaza",
"Town": "Buckinghamshire", "Contact": "Post", "IsFeatured": false,
"Manager": { "FirstName": "Ana", "LastName": "Frank" },
"Employees": [ { "FirstName": "Mark", "LastName": "Anderson" },
{ "FirstName": "John", "LastName": "Haus" },
{ "FirstName": "Sinthia", "LastName": "Clouny"}]
}
]
As you can see, in the array of companies that is returned, for each company object, both simple properties (Name
, Address
, etc.) and a nested object Manager
and a nested array Employees
are returned. Nested structures can also be displayed using client-side engines.
If loadJSON
is used, the HTML template should have nested elements that match the structure of the returned JSON response. An example of an HTML template that can be used with the loadJSON
plug-in is shown in the listing below:
<ol>
<li>
<div class="divEmployees">
<h3>Employees <img class="closed"
src="/Content/images/details_open.png" alt="open/close"/></h3>
<dl class="Employees">
<dt class="FirstName"></dt><dd class="LastName"></dd>
</dl>
</div>
<a href="details.html" class="ID">
<span class="Name"></span></a>
<span class="Address"></span>
<h3>Manager:</h3>
<span class="Manager">
<span class="FirstName"></span> <span class="LastName"></span>
</span>
</li>
</ol>
In the template are placed placeholders for the company fields (ID
, Name
, Address
) as in the list example. There is a placeholder Manager
(in the SPAN
tag) for the reference to the manager object that has two placeholders for the manager's first name and last name. Also, there is a place holder for the Employees
array in the DL
tag where the array of Employee
objects associated to the company via the "Employee
" array will be loaded. In that placeholder, nested placeholders for the first name and last name of each employee that is returned in the nested array are placed. When a JSON object is loaded in this template using the loadJSON
function, a nested structure of the element (moved to the right side) is generated as shown in the following figure:
On the first level, companies in the ordered list are listed, where basic company details and the nested manager object are shown. For each company, a nested list of the employees that work for the company is generated. In the screenshot above, employees for the first and fourth company are expanded while the others are collapsed. You can see a live example here, but without buttons for showing/hiding employees. The functionality for showing/hiding the nested lists of employees is implemented using custom JavaScript that is not part of the templating engine - you can find more details about this script below.
Alternative Implementation Using the Pure Templating Engine
Instead of loadJSON
, you can use any other templating engine. If you use Pure, the same HTML template can be used but you will need to define mapping rules between the hierarchical template and the JSON object that is returned from the server-side. If Pure engine is used, the following script generates the view:
$.getJSON('/Company/List', function (data) {
$("ol").render(data, {
li: {
'company<-': {
'.ID': 'company.Name',
'.ID@href': 'company.ID',
'.Address': 'company.Address',
'.Manager .FirstName': 'company.Manager.FirstName',
'.Manager .LastName': 'company.Manager.LastName',
'dl.Employees': {
'employee<-company.Employees': {
'.FirstName': 'employee.FirstName',
'.LastName': 'employee.LastName'
}
}
}
}
});
});
In this example, the JSON object is taken from the '/Company/List' URL and the returned JSON object is rendered into the OL
element that contains the template. The only difference is the directive that explicitly defines the mapping rules - I will try to explain the directive that is used in this example. The Render
function finds an li
element and iterates over the array that is returned by the 'company<-
' directive. Each object in the loop will be identified as a 'company
'. In the elements with classes ID
and Address
, the company.Name
and company.Address
fields from the array are placed, and in the attribute href
of the link with class "ID
" is placed the ID of the companies. In the elements with classes "FirstName
" and "LastName
" that are placed within the element with class "Manager
" are placed company.Manager.FirstName
and company.Manager.LastName
(properties of the nested object manager).
The inner list is generated using the last statement in the directive. In the DL
element with class "Employees
" is applied a loop over the array company.Employees
- each element in the loop is identified using the 'employee
' identifier. In the elements DT
and DD
with classes "FirstName
" and "LastName
" that are placed inside the DL
element are placed the employee.FirstName
and employee.LastName
properties of the JSON objects.
The Pure engine is a more powerful templating engine than the loadJSON plug-in because it has complex directives that enable you to customize mapping rules exactly how you need them. However, as you can see, you will need to learn the syntax that is used for the directives to be able to customize the rendering rules. An explanation of the syntax is not part of this article (this was just a specific example) so if you are interested in this, please find more details about the syntax at the Pure site.
Applying Custom JavaScript Code
Note that the view engine gives you just a static HTML structure - if you need some interaction, you can add a custom function to this structure. As an example, in this view, functionality is added that opens and closes the nested employees list each time the user clicks on the image. An example of the code for showing/hiding a nested list is shown in the following listing:
$("dl.Employees").hide();
$(".closed").click(function (item) {
if ($(this).hasClass("closed")) {
this.src = "/Content/images/details_close.png";
$(this).removeClass("closed");
$("dl", $(this).parents("li")).fadeIn("slow");
} else {
this.src = "/Content/images/details_open.png";
$(this).addClass("closed");
$("dl", $(this).parents("li")).fadeOut("slow");
}
});
This code initially hides all the nested employees and attaches the click handler on the image where employees for the selected company are shown/hidden depending on the state. This is completely independent of the template engine code, so the same functions can be used with different view engines as long as they generate identical output. There are no constraints in this approach; as long as you know what structure you need, you can apply any JavaScript code on the structure that is generated by the templating engine. Instead of the custom code, you can also apply some of the existing plug-ins that will continue to handle interaction with client.
As an example, if you need an expandable table, you can use the DataTables plugin that already handles most of the common functionalities you need. In this case, templating engines can be used to generate the initial HTML structure required by the DataTables plug-in, and then you can apply this plug-in on the generated structure and let the plug-in handle all further custom interactions.
Conclusion
This article explains how the loadJSON
plug-in can be used as a client-side templating engine, and it shows how JSON objects can be loaded directly in the HTML elements on the client side. There are several benefits of using client-side templating engines:
- Performance - loading JSON objects from the server side, only a minimal amount of data is transferred from the server to the client side. This way, it is not required to load unnecessary HTML tags from the server and the time required to load data is shorter.
- Caching - the example uses static HTML pages as views. Therefore, if active views (e.g., .aspx pages) are used instead, they can be cached on the server side. This makes the server-side response time shorter.
- True separation of design and code - using server-side templating engines, the code placed in the view is not completely clean. The HTML code contains some elements that are required for binding server side data to the view elements. The examples in this article show that the view is completely clean - there is nothing else except the HTML code in the view pages (not even custom attributes).
However, this is not a "Holy Grail" of web development as it does not resolve all your problems, nor does it represent the perfect solution. If you have some complex logic you need to place in the view, e.g., determine whether some element should be disabled or hidden, or if you need to dynamically show/hide some elements, you cannot implement this using simple client-side view engines. In such cases, it is better to retain server-side code data because it will be easier to control it via code, partial views, and server-side controls. However, applying client-side view engines (when this is appropriate) would bring you several benefits as explained above.
In this example, the loadJSON plug-in is used because it is the simplest plug-in that can be used and is the most appropriate for the presentation of client-side view engine concepts. However, you can use any other templating engine and the approach will be similar. Other templating engines I recommend are:
- jsRender - this is a successor of the deprecated jQuery templates which were part of the standard jQuery API.
- Pure - an excellent templating engine that provides a complex language for defining rules for matching elements in the template with a JSON data source. The drawback of this engine is that you will need to learn the templating language used in directives that define how data is bound to the template.
- Knockout - the most advanced template I found with support for binding, change tracking/update of JSON model, etc. If you want full control with your template engine, I would recommend this one.
There is no best templating engine and each of the engines mentioned in this article have both advantages and disadvantages. The major advantage of the loadJSON plug-in is its simplicity and ability to correctly populate form elements without specifying any rules for mapping. However, the same thing can be achieved in other templating engines with more or less effort.
- 28th April, 2011: Initial version