This article will show you how to integrate three different yet vital parts of a web application, namely, Client Layer, Server Layer and Service Layer.
Introduction
Web application development trends are changing for the good every now and then. The kinds of application architectures that we used to build some years ago have now started or already become somewhat obsolete and less efficient. The article that you are about to read applies to the same changing trends.
This article sheds light on integrating three different yet vital parts of a web application; namely Client Layer, Server Layer and Service Layer. It may be wrong to call Client and Server areas of code as layers but for the sake of uniformity in naming convention, let's think of them as specific layers of a multi-layered system in which the server sits on top, then service and then the client (You may even reverse the order if you wish and that would be perfectly alright).
Intended Audience
I am going to assume that you are already familiar with using Web API and maybe Handlebars. It would be great if you have worked on a large application which could amount to enterprise scale as then it would be easier for you to relate the separation of concerns that I have tried to implement in the sample code.
What to Expect
This article in no way tries to bring out some new form of web application development pattern. Web templating has been around for a very long time, only the way we handle the content generation from the templates has been changed. We used to have server side variables and embedded code put in the HTML markup in different places to achieve data binding with the templates. Even today, so many of us would prefer and rely heavily on dynamic code generation technologies like Razor View in ASP.NET MVC which is understandable as it perfectly addresses content generation and view model binding. But if you are a simple minded developer like me who is not very comfortable in mixing up things so much, then you would understand how easy it is to maintain an application in which we properly separate everything. And besides, I always feel that it's a bad idea to put too much domain logic inside the template code.
This article aims to provide a web templating code implementation using JavaScript and Web API, so that the developers who are looking to find a basic understanding of how things move in such an application can take a reference from here.
You can easily find other such code samples on the internet if you are willing to do a quick Google search but it is always better to have references of multiple ways of doing the same thing as each one of us has different requirements.
Sample Code Overview
The code in this article is for a sample shopping cart layout. In this application, we basically have to update the information inside a predefined web page design. For that purpose, it is best to use HTML templates as we only need to combine the input data with the template and display the compiled HTML result.

Overview
I have tried to keep things simple and my focus is mostly on Web API service calling and code generation from the compiled templates along with properly separating everything. You will not find any code for the database CRUD operations, for that purpose, I have created a very simple data mapper to use In-Memory objects. You are by all means free to code the data mapper to support additional providers as you like in the attached sample code.
The beauty of this implementation is that a specific type of data mapper is being injected into the Web API controller so that we can easily switch between development data and testing data. This approach makes it very easy for us to integrate our UI with a separate testing module. All we need to do is to use a different Web.config file to use a different data mapper. The mapper can then return data from any source like In Memory Objects, XML Stream or a test database.
This article is going to be somewhat long so I would request you to bear with me and you will find how you can properly structure your web application framework and you will have to write less code to add new features while abstracting out common stuff.
Let's first get to know about Handlebars templating engine in brief and also an introduction about what templates are all about. I am going to assume that you are already familiar with ASP.NET applications and Web API and also some basic understanding of templating. If you are not, then I would suggest to first get yourself acquainted with them before you further read this article.
Handlebars and Templating
Templates have been in use as an architectural practice for a very long time. Templates simply denote an output blueprint or wireframe in which there are tokens which we replace with actual values contained inside input data. The template is then merged with the data to generate the desired output.
For Handlebars, I am just going to use the definition given on Wikipedia as it is already very clear and I think there is no need for me to re-invent another one.
Quote:
Handlebars is a semantic web template system, started by Yehuda Katz in 2010. Handlebars.js is a superset of Mustache, and can render Mustache templates in addition to Handlebars templates. While Mustache is a logicless templating language, Handlebars adds extensibility and minimal logic, such as #if, #unless, #with, and #each helpers.
Using the Code
Starting Out
Before we start understanding the client code, we should first know how things are moving in the server code. The sample code has a separate Data layer which contains classes to get the data from whatever data source you decide to use. I have created an interface IMapper
and have put it inside another layer which is shared across everywhere so that we can manage dependency injections across different parts of the application.
Step 1
We will start by creating an empty ASP.NET Web Forms Application in Visual Studio 2012 (You can download one from here). This empty application does not have any pre-generated code related to navigation or authentication and that is exactly what we need for this sample application.

Here is a snapshot of the project structure. I am going to explain all of the modules in it one by one:

Add a new .aspx file and name it Default.aspx. Also, add a master page and name it Master.master
. As of now, we do not need to add any code to the master page, we will later add references to the scripts and stylesheets that we will need.
Replace the code inside Default.aspx file that we just added with the following:
%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
Inherits="TestCart.Default" MasterPageFile="~/Master.Master" %>
<asp:Content ID="index" runat="server" ContentPlaceHolderID="MainPlaceHolder">
<div id="divGrid">
</div>
<script>
</script>
</asp:Content>
The default page will contain the grid of all the available products.
Add another aspx page and name it Product.aspx. We will only have two aspx pages in this sample application and they will be enough to convey the understanding of doing templating in ASP.NET using Handlebars. Add the following code to the Product.aspx page.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Product.aspx.cs"
Inherits="TestCart.Product" MasterPageFile="~/Master.Master" %>
<asp:Content ID="product" runat="server" ContentPlaceHolderID="MainPlaceHolder">
<div id="divProduct">
</div>
<script>
</script>
</asp:Content>
Before we dive further into the client side, let's first get to know about the server code structure. The client will simply call the web Api to get the data and will then combine the data with the templates. Now we can follow one of the two paths:
- First one is to write our domain logic directly into the Web Api class, initialize the data access module in Global.asax file and tie it to the session. Web Api class will use that data access module in the session to get the data. This approach has a lot of drawbacks like there should not be any business logic inside the service layer (or it should be bare minimum) and we are also tightly coupling the services with the data access module making it extremely difficult to integrate a test module or some other production data provider.
- Second one is when we are injecting the appropriate data access module dependency into the Web Api and not putting any domain logic code inside it.
Now obviously you guys have figured out which approach is better so let's start to implement the second one.
Add a new class library project and name it Data
. Add a class in the Data
project and also name it Data
. (See the Solution Explorer image above).
In the Data
class, we will first take care of our in memory product repository. The repository will be used to store static product objects in the memory. For that purpose, add the following code into Data.cs file:
#region Static Data
public class ProductRepository
{
private List<Product> _products;
public List<Product> Products { get { return _products; } }
public ProductRepository()
{
Product product;
_products = new List<Product>();
product = new Product(1, "iPhone 6", 38999, "iphone.jpg",
"Apple iPhone 6 exudes elegance and excellence at its best. With iOS 8,
the world’s most advanced mobile operating system, and a larger and
superior 4.7 inches high definition Retina display screen,
this device surpasses any expectations you might have with a mobile phone. ",
"This new generation device comes with improved Wi-Fi speed to enhance
your surfing experience. Its sleek body and seamless design along with
a perfect unison between its software and hardware system gives you
marvellous navigation results. Just double tap the home screen
and the entire screen shifts down to your thumb for easy operation.
In terms of security, this iPhone is a class apart from its predecessors
as it allows you finger print lock. The multitude of features in
Apple iPhone 6 makes it powerful, efficient, smooth and super easy to use.
With this phone in your hands, you can manage your world with
just a single touch!",
true);
product.Reviews = new List<Review>();
product.Attributes = new List<ProductAttribute>();
product.Reviews.Add(new Review("John", "Very good phone!!", 4));
product.Attributes.Add(new ProductAttribute("Brand", "Apple"));
_products.Add(product);
}
public Product GetById(Int32 id)
{
return _products.Find(p => p.Id == id);
}
}
#endregion Static Data
The above is a very ugly way to add in-memory objects but that will do for this sample application. In the ProductRepository
class constructor, we are adding product information to the list Products
. The attached code has a lot more products being added to the list. The view model classes for Product
, Reviews
and Attributes
are contained in a Shared module.
The product list can be accessed directly from the read-only Products
property. There is a method GetById
which will search a product in the product list using the integer type Id and will return the product
object if found.
Now we need to add abstract and concrete mapper classes to get the data. Before we do that, we need to add another logical layer in our solution for the shared data. Add a new class library project and name it Shared
. Add three classes in the Shared layer and name them Models
, SharedInterfaces
and Types
.
Let's add the code for our view model classes as follows in the Models
class:
public class Product
{
public Int32 Id { get; set; }
public String Name { get; set; }
public Decimal Price { get; set; }
public String Image { get; set; }
public String SmallDescription { get; set; }
public String LargeDescription { get; set; }
public List<Review> Reviews { get; set; }
public List<ProductAttribute> Attributes { get; set; }
public Boolean InStock { get; set; }
public Product(Int32 id, String name, Decimal price, String image,
String smallDescription,
String largeDescription, Boolean inStock)
{
Id = id;
Name = name;
Price = price;
Image = image;
SmallDescription = smallDescription;
LargeDescription = largeDescription;
InStock = inStock;
}
}
public class Review
{
public String ReviewedBy { get; set; }
public String ReviewText { get; set; }
public Int32 Rating { get; set; }
public Review(String reviewedBy, String reviewText, Int32 rating)
{
ReviewedBy = reviewedBy;
ReviewText = reviewText;
Rating = rating;
}
}
public class ProductAttribute
{
public String Name { get; set; }
public String Value { get; set; }
public ProductAttribute(String name, String value)
{
Name = name;
Value = value;
}
}
The above code is self explanatory, we are just adding simple classes with public
properties to hold the product data. These view models will be returned to the JavaScript code and then will be merged with our templates.
We are going to need interface contracts to be shared across the application to perform dependency injections. So it makes sense to put them in a common place. Add the following code to the SharedInterfaces
class:
public interface IMapper
{
Object Get(DataType type);
void SetId(Int32 id);
}
public interface IProductMapper
{
List<Product> GetAllProducts();
Product GetProductById(Int32 id);
}
IMapper
is the interface for all the data mappers. Get
method will return the data based on the type. SetId
method is to set the id of record that we need. DataType
is an enum
which will direct the Get
method to fetch the specific information that we need.
There is another interface IProductMapper
which is specific to the products. The IMapper
will be injected into the IProductMapper
constructor. IProductMapper
method implementations will then call IMapper
methods internally to perform crud operations. GetAllProducts
will get all the available products and GetProductById
will return a single product based on the id provided. When we use a database, then the concrete product mapper method implementations will need to be wrapped inside transactional scopes.
Only thing left in Shared is to add the DataType enum
implementation:
public enum DataType
{
AllProducts,
IndividualProduct
}
As of now, we only need two types; AllProducts
and IndividualProducts
. The current mapper implementation needs to handle all the types to return the type of data being requested. You are free to add more as per your convenience.
With the shared code in place, now we can move to implement the concrete mapper classes. We will be having three mapper classes: AbstractMapper
, InMemoryMapper
and ProductMapper
.
AbstractMapper
will contain the code common to all the different concrete mappers. Add the following code to Data.cs class implement this:
#region Mappers
public abstract class AbstractMapper : IMapper
{
protected Int32 _id;
public abstract Object Get(DataType type);
public void SetId(Int32 id)
{
_id = id;
}
}
In the above code, we have declared the Get
method as abstract
so that the concrete classes inheriting the abstract
mapper can do the implementation. The SetId
method is just setting the value of the protected _id
variable.
InMemoryMapper
will do the job of getting the data from the in-memory data that we will create by instantiating the ProductRepository
class. The following code is for this mapper class:
public class InMemoryMapper : AbstractMapper
{
public override Object Get(DataType type)
{
Object retVal = null;
switch (type)
{
case DataType.AllProducts:
retVal = new ProductRepository().Products;
break;
case DataType.IndividualProduct:
retVal = new ProductRepository().GetById(_id);
break;
}
return retVal;
}
}
Above is the implementation of the Get
method. Based on the type of data being requested, the code initializes a new instance of the ProductRepository
and then gets the data. One thing to note is that you can set the in-memory data as static
to avoid creating new objects every time; this is an exercise I leave for you guys to work on.
ProductMapper
is the specific mapper for the products. It will use the IMapper
interface to execute different code instructions to get the data that the users need. Add the following code to the Data.cs file:
public class ProductMapper : IProductMapper
{
private IMapper _mapper = null;
public IMapper Mapper
{
get { return _mapper; }
set { _mapper = value; }
}
public ProductMapper(IMapper mapper)
{
_mapper = mapper;
}
public List<Product> GetAllProducts()
{
return _mapper.Get(DataType.AllProducts) as List<Product>;
}
public Product GetProductById(Int32 id)
{
_mapper.SetId(id);
return _mapper.Get(DataType.IndividualProduct) as Product;
}
}
The above code is straightforward if you are familiar with how the simplest dependency injections work. If you are not, then I would strongly suggest to read about it.
What we are doing is just injecting a specific implementation of the IMapper
interface into the ProductMapper
constructor. The ProductMapper
only needs to know about the members of the IMapper
and not the type of implementation. We will then inject this implementation of ProductMapper
in our Web Api controller.
We have created all the required classes to get the product data, now is the time to move on to the Web Api. Add a new Web Api controller and name it ProductController
. It will come with empty GET
/PUT
/POST
/DELETE
methods. We will only need the GET
methods to get the data from the server. Add the following code into the ProductController
for a constructor function which accepts a mapper of type IProductMapper
as an argument. We also need to add a private
field to hold the mapper reference and modified signatures for the GET
methods:
public class ProductController : ApiController
{
private readonly IProductMapper _mapper;
public ProductController(IProductMapper mapper)
{
_mapper = mapper;
}
public List<Shared.Product> Get()
{
return _mapper.GetAllProducts();
}
public Shared.Product Get(int id)
{
return _mapper.GetProductById(id);
}
public void Post([FromBody]string value)
{
}
public void Put(int id, [FromBody]string value)
{
}
public void Delete(int id)
{
}
}
The Api controller class will use the private
_mapper
to call the IMapper
type implementation of the concrete mapper class. The methods don't need to worry about what type of mapper is being used and so the UI is completely unaware and un-plugged from the data provider. This makes it very easy to integrate custom test suits with our application.
Now the question arises that how are we going to actually inject the mapper in the web Api controller class. We are going to do this by creating an implementation of the IDependencyResolver interface. IDependencyResolver
is used to resolve the Web Api service dependency and we can return the service based on the requested service type. This is illustrated in the following class. Add a new folder App_Code and a class MyDependencyResolver
and add this code into the class file:
public class MyDependencyResolver : IDependencyResolver
{
private static readonly IProductMapper Mapper =
new ProductMapper(DataHelper.CreateMapper(WebConfigurationManager.AppSettings
["MapperType"].ToString()));
public Object GetService(Type serviceType)
{
return serviceType == typeof(ProductController) ?
new ProductController(Mapper) : null;
}
public IEnumerable<Object> GetServices(Type serviceType)
{
return new List<Object>();
}
public IDependencyScope BeginScope()
{
return this;
}
public void Dispose()
{
}
}
The GetService
method implementation will be used to return the appropriate Api Controller based on the type of service requested. When the requested type is ProductController
, then we are returning a new instance of ProductController
class by injecting the proper mapper dependency based on the mapper type stored in the config file.
The value of mapper type needs to be stored in the web.config file:
<appSettings>
<add key="MapperType" value="InMemoryMapper" />
</appSettings>
Now we need to wire-up the dependency resolver with the application configuration, this can be done in the Global.asax file as follows:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
GlobalConfiguration.Configuration.DependencyResolver = new MyDependencyResolver();
}
}
Oh and we will also need to map the api controller route template with the route table when we are explicitly adding Web Api controller to a web forms application.
We are still missing a vital piece of code which will be used to create the mapper type instance using the String
value stored in the config. Add this code in the Data.cs file:
#region Helper
public static class DataHelper
{
public static IMapper CreateMapper(String type)
{
return Activator.CreateInstance(Type.GetType
(String.Format("Data.{0}", type))) as IMapper;
}
}
#endregion Helper
Above, we are using reflection to create the mapper and type and then type casting it into IMapper
before returning the object. This mapper
object will be then passed into concrete product mapper class constructor.
This was all about the server code, now we are at liberty to move on to the client code. You must have already added the master and aspx pages and we have to now add the supporting JavaScript code. We should do this by creating an application structure in the JavaScript which should be easily accessible from everywhere. To do this, I am just going to attach an object named App
to the window. From our App
object, we can do different things like calling the Web Api, using Handlebars templates, Event Binding, etc.

I am going to add separate JavaScript files associated with different tasks so as to make future code changes easier. To load the App
object into the window, I am just going to use a simple loader function. Similarly, I will use separate loader functions for each module.
Add a new JavaScript file into Content\Scripts folder and name it App.js:
;
(function (w, undefined)
{
function loadApp()
{
var app =
{
'Events': new w['AppEventsLoader'](),
'Service': new w['AppServiceLoader'](),
'Constants': new w['AppConstantsLoader'](),
'Templates': new w['AppTemplatesLoader']()
};
w['App'] = app;
if (w['LoadApp']) w['LoadApp'] = undefined;
if (w['AppEventLoader']) w['AppEventLoader'] = undefined;
if (w['AppServiceLoader']) w['AppServiceLoader'] = undefined;
if (w['AppConstantsLoader']) w['AppConstantsLoader'] = undefined;
if (w['AppTemplatesLoader']) w['AppTemplatesLoader'] = undefined;
}
w['LoadApp'] = loadApp;
})(window);
After the loaders are done doing their jobs, we are going to remove them from the memory as we are not going to use them anymore. At this point, you must be thinking how we are going to call the LoadApp
function, you will find this out when we will get to the master page implementation below.
Let's add the different modules of our App
object. Add a new JavaScript file and name it Service.js. We are going to use it to call the Web Api using a single point of entry.
;
(function (w, $)
{
var serviceLoader = function ()
{
this.Call = function (type, args)
{
var url = w.App.Constants.ServiceMap[type];
if (!args) return $.getJSON(url);
else return $.getJSON(url, args);
};
};
w['AppServiceLoader'] = serviceLoader;
})(window, jQuery);
In the above code, type
is used to determine the Web Api url. If args
is not null
, then we will send it with the service call. I have stored the metadata information of service type and template url in the code which will get loaded at the time of App initialization. Constants.js is used for this purpose:
;
(function (w)
{
var constantsLoader = function ()
{
this.ServiceMap =
{
'GetAllProducts': '/api/Product/',
'GetProduct': '/api/Product/'
};
this.TemplateMap =
{
'ProductGrid': 'Content/Templates/ProductGrid.html',
'ProductItem': 'Content/Templates/ProductItem.html'
};
};
w['AppConstantsLoader'] = constantsLoader;
})(window);
There is one last JavaScript module which we will use to compile the Handlebars templates. Now before we try to implement the code, we should make sure how we are going to put everything together; so here it goes. We need:
- input data
- handlebars template which is nothing but HTML string with tokens in the form of
{{value}}
here and there, and - lastly, we need to combine the two to get our final HTML that will be shown to the user. It means that the output HTML will always be based on the user's requested information.
For example, if we have input data as follows in the form of Json:
var data = {'FirstName': 'Mark', 'LastName': 'Twain'};
and we have the following handlebars template:
<div id='placeholder'></div>
<script id="my-template" type="text/x-handlebars-template">
<div>First Name: {{FirstName}}</div>
<div>Last Name: {{LastName}}</div>
</script>
then we will write the following JavaScript code to combine the template with the data:
var myTemplate = Handlebars.compile($('#my-template').html());
var myHtml = myTemplate(data);
$('#placeholder').html(myHtml);
We can also bind json object arrays with the templates, for that purpose, we need to use {{#each}}
token as given in the following example:
var contact = {'Name': 'Jason', 'Phones':[{'Number': 111111}, {'Number': 222222}]};
<script id="my-template" type="text/x-handlebars-template">
<div>Name: {{Name}}</div>
Phone Numbers: <br />
{{#each Phones}}
<div>Last Name: {{Number}}</div>
{{/each}}
</script>
It's simple as that. However, we are not going code things this simple in our sample app. We will load the template into the browser cache whenever we are going to need it and then will use the input data to get the output HTML.
Add a new JavaScript file and name it Templates.js and paste the following in it:
;
(function (w, $, h)
{
var templatesLoader = function ()
{
this.GetTemplateHtmlAjax = function (name, jsonData, secondCallback)
{
var source;
var template;
var html;
$.ajax({
url: App.Constants.TemplateMap[name],
cache: true
}).then(function (data)
{
source = data;
template = h.compile(source);
html = template(jsonData);
secondCallback(html);
});
}
};
w['AppTemplatesLoader'] = templatesLoader;
})(window, jQuery, Handlebars);
GetTemplateHtmlAjax
function accepts the template name
, jsonData
and the callback
as arguments. The template name is used to resolve the template file path, jsonData will be provided as the source for the loaded and compiled template and the secondCallback
function will be called along with passing the output HTML.
So when we want to bind input data with a template, then we simply have to call the above GetTemplateHtmlAjax
and will put the HTML writing code inside the callback function which will be executed once we have the HTML response available.
Our small client side framework is almost complete, all we need to do now is to define our template HTML and put everything together. Add two HTML files to the Templates folder (see the solution explorer image above) and name them ProductGrid.html and ProductItem.html.
ProductGrid.html
<div>
{{#each Products}}
<div class="ProductGridItem">
<div>
<div style="display:block;overflow:hidden;margin:0 auto;">
<a href="Product.aspx?id={{Id}}">
<img src="../../Content/Images/{{Image}}" /></a>
</div>
</div>
<hr />
<div><a href="Product.aspx?id={{Id}}"><b>{{Name}}</b></a></div>
<hr />
<div style="text-align:center;"><b>Rs. {{Price}}</b></div>
<hr />
<div><a class="AddToCartGridButton" href="#">Add To Cart</a></div>
</div>
{{/each}}
</div>
ProductItem.html
<div style="overflow:hidden;">
<div class="ProductItemLeftDiv">
<img src="../../Content/Images/{{Image}}" />
</div>
<div class="ProductItemRightDiv">
<div><h2>{{Name}}</h2></div>
<hr />
<b>Price</b> {{Price}}
<hr />
{{SmallDescription}}
<hr />
</div>
</div>
<div id="divLargeDescription">
<h3>Description</h3>
<hr />
{{LargeDescription}}
</div>
<div id="divAttributes">
<h3>Attributes</h3>
<hr />
<table>
{{#each Attributes}}
<tr>
<td><b>{{Name}}</b></td>
<td>{{Value}}</td>
</tr>
{{/each}}
</table>
</div>
<div id="divReviews ">
<h3>Reviews</h3>
<hr />
{{#each Reviews}}
<span class="ReviewedBySpan"><b>{{ReviewedBy}}</b></span>
<span class="RatingSpan">Rating: {{Rating}}</span>
<div>{{ReviewText}}</div>
{{/each}}
</div>
We are going to need Handlebars and jQuery library files. Although jQuery is not mandatory, most of us end up using jQuery at some point to avoid handling every code scenario for various browsers.
Download the latest Handlebars and jQuery library scripts and add their reference in the master page (alternatively, you can use cdn links as they might have already been cached by the browser). We also need to add references of all the JavaScript files, and a separate script block to initialize our client App
object.
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Master.master.cs"
Inherits="TestCart.Master" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<link href="Content/Style/Style.css" rel="stylesheet"/>
<script src="Content/Scripts/jquery-1.11.3.min.js"></script>
<script src="Content/Scripts/handlebars-v4.0.2.js"></script>
<script src="Content/Scripts/App.js"></script>
<script src="Content/Scripts/Constants.js"></script>
<script src="Content/Scripts/Events.js"></script>
<script src="Content/Scripts/Service.js"></script>
<script src="Content/Scripts/Templates.js"></script>
<script>
$(document).ready(function ()
{
if(!window.App) window.LoadApp();
});
</script>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder ID="MainPlaceHolder" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
In the above code, we are calling LoadApp
function to create the App
object if it is not already there in the browser memory.
Add the following script block to Default.aspx to load the product grid:
<script>
$(document).ready(function ()
{
var app = window.App;
if (app)
{
app.Service.Call('GetAllProducts').then(function (data)
{
$divGrid = $('#divGrid');
function setHtml(html)
{
$divGrid.html(html);
}
app.Templates.GetTemplateHtmlAjax('ProductGrid'
, { 'Products': data }, setHtml.bind(this));
})
}
});
</script>
The above code should be easy to understand. We are calling the GetAllProducts
web Api service and in the success callback, we are calling the GetTemplateHtmlAjax
function from the Templates module to get the output html string. The setHtml
is the callback function which is passed as an argument to GetTemplateHtmlAjax
and gets called after a HTML response is available. The path of web Api service will be resolved from the metadata information that we coded into the Constants.js file.
One thing to note here is that we are using bind on setHtml
function to preserve the function scope. If you are unaware of what that means, then you should learn more about scopes in JavaScript as this is outside the scope of this article (no pun intended!).
Similarly, add the following script block to the Product.aspx file:
<script>
$(document).ready(function ()
{
var app = window.App;
if (app)
{
app.Service.Call('GetProduct', { 'Id': getParameterByName('id') }).then
(function (data)
{
$divProduct = $('#divProduct');
function setHtml(html)
{
$divProduct.html(html);
}
app.Templates.GetTemplateHtmlAjax('ProductItem', data, setHtml.bind(this));
})
}
function getParameterByName(name)
{
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
});
</script>
getParameterByName
is there to read the value of any query string parameter in the url. We are reading the value of id
from the url which is then passed as an argument to the web api service call.
So that was it, now all you need to do is download the sample code and run it if you haven't already and relate all the steps given here with the sample code to get a much better understanding of how everything is working. I have not gone into much detail about Handlebars or Web Api in general as my intention was to give a working sample on which you can base your code or maybe you can get a better view from a different perspective on how to design your application when implementing HTML templates.
If you think that I have missed something or I need to explain anything differently, then feel free to drop your comment and I will be more than willing to fix it.
History
- 12th November, 2015: Initial version