Use of KnockoutJS Template Feature

In this article I will try to cover the usage of KnockOut JS template feature. If you want to learn basics of knockoutJS then the tutorial (http://learn.knockoutjs.com/#/?tutorial=intro) from knockout website is best place to start with.

 So here we are creating a simple website with help of knockoutJS template feature and other libraries e.g. jQuery, bootstraps etc. The website is simple, it has left navigation and once you click on the link respective article will get opened.

The final look of the website is depicted below.

website final look

Prerequisite: To understand this application, you should have basic understanding of following topics.

  • Basic understanding of HTML, JavaScript, CSS and JQuery.
  • Very basics of KnockoutJS libraries, you can quickly learn about knockoutJS from their training website as I have mentioned at the starting of the article.
  • I am using bootstrap for layout of the website.
  • I am using css for the look and feel of the website.

 

So before starting this example let’s discuss template feature briefly of knockoutjs.

Template Binding is a wonderful feature for populating DOM element with the results of rendering template.

Template feature provides you convenient way for building sophisticated UI structures -possibly with repeating or nested blocks - as a function of your view model data.

To know more about template-binding using knockoutJS refer their own article/tutorial (http://knockoutjs.com/documentation/template-binding.html ) .

Coming to the folder structure of the application, I am following the Progressive Enhancement pattern.

Below is the list of folders and their contents

css : contains css files (files used for managing look and feel of the website)

js: contains JavaScript code being used in the applications, you can further organize it based on the module, if your application is big.

data: This folder contains application data being used by the website, I have created a file (JSON) which contains website’s information, generally these kind of information needs to  be stored in database etc. 

images: Image folder used for images being used for website.

lib: lib folder contains js libraries being used by the website.

views: View folder contains all html files being used by the website and it is further structured by sub-folder as per the contents of the website.

So let's go through the files which are base for the website.

home.html

This is the main file where the complete layout of the website is defined. The website’s layout has (Header, Left Navigation, Content section, Right side navigation (If you want to add anything) and footer).  Nothing much to talk about this file, the code itself is self explanatory (class name "col-md-*" is part of bootstrap css).

<!DOCTYPE html>
<html lang="en">
	<head>
	
		<link rel="stylesheet" type="text/css" href="css/jquery-ui.min.css" />
		<link rel="stylesheet" type="text/css" href="css/jquery-ui.structure.min.css"/>
		<link rel="stylesheet" type="text/css" href="css/jquery-ui.theme.min.css"/>
		<link rel="stylesheet" type="text/css" href="css/demo.css" />
		<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
		<link rel="stylesheet" type="text/css" href="css/bootstrap-theme.min.css" />
	</head>
	
	<body>
	<div id="main-body">
		<div id="body-content" class="container">
			<div class="row">
				<!-- Header Navigation section -->
			   <div class="col-md-12" id="header-nav"> 
			   </div>
			</div>
			
			<div class="row">
				<!-- Left Navigation section (It's upto you how you want to represent your left navigation)-->
			   <div class="col-md-2" id="left-nav">
			   </div>
				<!-- Center section used for displaying content you have selcted 
				from the left navigation section -->		
			   <div class="col-md-8">
				<div class="mycontainer">
					<div id="container-example">
					</div>
					  
				</div>
			  </div>
			  <!-- This section you can use for anything, I have mentioned to show Advertisement on your website  -->
			   <div class="col-md-2">
				<div id="advt-container">
							
				</div>
			  </div>
			</div>
			 <!-- Footer section  -->
			<div class="footer">&copy; Deepak Singh</div>
		</div>
	  </div>
	</body>
	
	<script type='text/javascript' src='lib/knockout3.1.js'></script>
	<script type='text/javascript' src='lib/jquery1.js'></script>
	<script type='text/javascript' src='lib/jquery-ui.min.js'></script>
	<script type="text/javascript" src='lib/bootstrap.min.js'></script>
	<script type="text/javascript" src='js/navhandling.js'></script> 
	
</html>

 

So let's see how we can use template binding feature of knockoutJS for rendering header information. Header information binding has been done with help of two files header.html and headermodel.js.

 headermodel.js

headerMessageViewModel = function(){
	this.headerMessage={
			headertitle: 'Knockout Template Example!!!!', 
			menus:[{title:"About Me", page:"about.html"}]
	};
	
}
ko.applyBindings(new headerMessageViewModel(), document.getElementById("container-header"));

 

In the above code, if you see I have created headerMessageViewModel  JavaScript variable which I am binding with DOM element id named "container-header". We would able to understand and connect this code very well when we will see header.html code. And also a complete integration (displaying all header information in header navigation section of main page) of header information with main template file (home.html).

header.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<!-- Creating headermenutemplate for displaying header menu -->
		<script type="text/html" id="headermenutemplate">
				
			<a href="#" data-bind="click:function(){$('#container-example').load('views/'+$data.page); }">
				<span data-bind="text:menu.title"> </span>
			</a>
				
		</script>
		<!-- Creating headertemplate for displaying header title and header menu 
			for displaying menus being used by the website
		-->
		<script type="text/html" id="headertemplate">
			<h3 data-bind="text:headertitle"> </h3>	
			<span id="header_menu" data-bind="template:{name:'headermenutemplate', foreach:menus, as:'menu'}">
			</span>
		</script>
		
        <script type='text/javascript' src='headermodel.js'></script>
	
    </head>
    <!-- This section of DOM is binded in headermodel.js. "headertemplate" is the name defined above in the script tag.-->
    <div id="container-header" class="header">
         <div class="header" data-bind="template:{name:'headertemplate', data:headerMessage}">
         </div>       
    </div>
    
</html>

 

So let's understand the each section of the above "header.html" code.

Below is the div section we are binding (ko.applyBindings(new headerMessageViewModel(), document.getElementById("container-header"));) in the headermodel.js file.
<div id="container-header" class="header"> <div class="header" data-bind="template:{name:'headertemplate', data:headerMessage}"> </div> </div>

Understand the template binding for displaying header title and menu in steps.

1. In heademodel.js ko.applyBindings() method is basically used for binding the 'headerMessageViewModel' JavaScript object to the specific dom element "container-header".
headerMessageViewModel function contains "headerMessage" object which in turns contains two properties "headertitle" and "menus" array.

2. Now in "header.html" of code section we are using "data-bind" (data-bind is the way to link data to the UI, please refer article http://knockoutjs.com/documentation/binding-syntax.html for more understanding) to whom we are telling template name and JavaScript object which we have bound in headermodel.js file (i.e. headerMessage).
So you can think of that, we are telling the headertemplate that use headerMessage data for extracting or rendering information.

<div class="header" data-bind="template:{name:'headertemplate', data:headerMessage}"> </div>

3. So in second step, now we know the template name and the data needed for the template (headertemplate). And from the below code you can understand how we are using headerMessage for displaying header title and passing menus data to another template.

<script type="text/html" id="headertemplate"> <!-- template name given above -->
<!-- here we are displaying header title, which is property of headerMessage object -->
<h3 data-bind="text:headertitle"> </h3>
<!-- here we are menus array, part of headerMessage object using data-bind to "headermenutemplate" -->
<span id="header_menu" data-bind="template:{name:'headermenutemplate', foreach:menus, as:'menu'}">
</span>
</script>

4. Here we are displaying menus defined.

<script type="text/html" id="headermenutemplate">
<!-- Here I am binding click event and opening the content in div section ("container-example") of home.html -->
<a href="#" data-bind="click:function(){$('#container-example').load('views/'+$data.page); }">
<span data-bind="text:menu.title"> </span>
</a>

</script>

So this is how header information has been displayed using knockoutjs template binding feature.

Understand the template binding for displaying left navigation as an accordion view

You have already seen the home.html code where it has section for left navigation. So now we will understand how we have to populate left navigation menu using knockout JS template binding.

left-nav.html

<!DOCTYPE html>
<html lang="en">
	<head>
		
        <script type="text/html" id="topicListTemplate">
			<!-- Binding click event on Menu title name -->	   
			<p class="menu-content-title" data-bind="click: function (){navlink($data.id, $data.page);} , attr: {'id': $data.id}">
				<strong>
					
					<span class="menu-text-middle" data-bind="text:topic.topicname"> </span> 
				</strong>
				
				<small> <span data-bind="html:topic.breifdesc"> </span></small>
			</p>
			
			<div class="menu-seprator"></div>
			
		</script>
		<!-- Displaying Menu title name-->
		<script type="text/html" id="techMenuTemplate">
			<h4 data-bind="text:name"> </h4>
			<div class="menu-content-override" data-bind="template:{name:'topicListTemplate', foreach:topiclist, as:'topic'}">
			</div>
		</script>
	    <script type='text/javascript' src='js/menucontainerviewmodel.js'></script>
	</head>
        
         <div id="menu-container"> 
                <div class="menu-content" id="menu-list" data-bind="template:{name:'techMenuTemplate', foreach: techlists, as: 'tech'}">
                </div>
         </div>
        
</html>

 

To make the left navigation dynamic, we have created JSON file which contains information such as name, topiclist array which in turns contain topic name, page (which will open in the content section of the main page), breifdesc (if you want to add any description), and id to identify each menu item uniquely. To add new topic, you have to just add the data in this JSON file. 

To make things more dynamic, you can use database to store all these information.

The structure of JSON file will look like below as mentioned in the allarticles.json file.

allarticles.json

{
	"topiclists": [
		
		{
			"name": "Java",
			"topiclist": [{
				"topicname": "Regular Expression",
				"page": "views/java/regex.html",
				"breifdesc": "",
				"id": "regex"
			}]
		},
		   {
			"name": "JavaScript",
			"topiclist": [{
				"topicname": "ECMA",
				"page": "views/javascript/ecma.html",
				"breifdesc": "",
				"id": "ecma"
			}, {
				"topicname": "Progressive Pattern",
				"page": "views/javascript/progressive.html",
				"breifdesc": "",
				"id": "progressive"
			}, {
				"topicname": "BOM/DOM",
				"page": "views/javascript/dombom.html",
				"breifdesc": "",
				"id": "dombom"
			}, {
				"topicname": "Scopes",
				"page": "views/javascript/scopes.html",
				"breifdesc": "",
				"id": "js"
			}, {
				"topicname": "Initialization",
				"page": "views/javascript/initialization.html",
				"breifdesc": "",
				"id": "scopes"
			}, {
				"topicname": "Method Chaining",
				"page": "views/javascript/method-chaining.html",
				"breifdesc": "",
				"id": "method-chaining"
			}, {
				"topicname": "Object Creation",
				"page": "views/javascript/obj-creation.html",
				"breifdesc": "",
				"id": "obj-creation"
			}, {
				"topicname": "Callback Function",
				"page": "views/javascript/callback.html",
				"breifdesc": "",
				"id": "callback"
			}, {
				"topicname": "Anonymous Function",
				"page": "views/javascript/anonymous.html",
				"breifdesc": "",
				"id": "anonymous"
			}, {
				"topicname": "Closure",
				"page": "views/javascript/closure.html",
				"breifdesc": "",
				"id": "closure"
			}, {
				"topicname": "Bubbling & Capturing",
				"page": "views/javascript/bubblingcapturing.html",
				"breifdesc": "",
				"id": "bubblingcapturing"
			}, {
				"topicname": "OOPs in JavaScript",
				"page": "views/javascript/oops.html",
				"breifdesc": "",
				"id": "oops"
			}, {
				"topicname": "Bind Method",
				"page": "views/javascript/bind.html",
				"breifdesc": "",
				"id": "bind"
			}, {
				"topicname": "Call and Apply",
				"page": "views/javascript/callandapply.html",
				"breifdesc": "",
				"id": "callandapply"
			}]

		}, {
			"name": "Other",
			"topiclist": [{
				"topicname": "Delicious",
				"page": "views/other/delicious.html",
				"breifdesc": "",
				"id": "delicious"
			}, {
				"topicname": "Job Interview",
				"page": "views/other/job-interview.html",
				"breifdesc": "",
				"id": "job-interview"
			}, {
				"topicname": "Blog",
				"page": "views/other/blogs.html",
				"breifdesc": "",
				"id": "blogs"
			}]
		}
    ]

}

 

So in the above JSON file, we have created three topics Java, JavaScript and Other. Now in coming steps let's understand how we can read this information for displaying the accordion in left navigation section.

We are loading allarticles.json file in menucontainerviewmodel.js this file also contain logic for handling css (i.e. changing menu selection color on hover).

menucontainerviewmodel.js

var menuModel = {};
/*
 Here we are reading the json file.
*/
 $.getJSON("data/allarticles.json", function(data) { 
        /* creating techlists property of menuModel object and making it observable 
	array with help of knockoutjs library.
		*/
        menuModel.techlists = ko.observableArray(data.topiclists);
		/* Here we are just bindung the menuModel to the menu-container div, 
		which is in home.html*/
        ko.applyBindings(menuModel, document.getElementById("menu-container"));
		 $(function() {
			 	$( "#menu-list" ).accordion({
				collapsible: true,
				heightStyle: "content"
				});
		});
		
		  $('.menu-content-title').hover(
				
               function () {
                  $(this).css({"background-color":"#00131a"});
               }, 
				
               function () {
		 if($(this).attr("clicked") != 'true')
		 $(this).css({"background-color": "#00384d;"});
               }
            );	
 });
 

 

I am using another JS file (navhadling.js) for handling the css logic (i.e. activating the link which is selected and loading default and other page on click of the particular menu item).

navhandling.js

 function navlink(id, path){
	$('#container-example').load(path); //load the page
	// handling css logic
	$(".menu-content-title").removeClass("active-link");
	$(".menu-content-title").css({"background-color": "#00384d;"});
	$(".menu-content-title").removeAttr("clicked");
	$("#"+id).removeAttr("style");
	$("#"+id).addClass("active-link");
	$("#"+id).attr("clicked", "true");
	$(window).scrollTop(0);
}

 $( document ).ready(function() {
	// loading menu and default page.
	  $( "#left-nav" ).load("left-nav.html");
	  $( "#header-nav" ).load("header.html");
	  $( "#container-example" ).load("views/about.html");
  });  

 

In menucontainerviewmodel.js we are binding menuModel to "menu-container" div created in left-nav.html

And in left-nav.html code you can see I have defined two template one for rendering the Technology name (i.e. techMenuTemplate) and another (i.e. topicListTemplate) for displaying the topic list.

So as I explained about I how I am displaying the header information, it has been done in the similar manner.

That's all about knockoutJS template binding.

Source Code: KnockOutTemplateExample 

Please feel free to comment and ask questions.

Reference:
http://knockoutjs.com/documentation/introduction.html
http://learn.knockoutjs.com/
http://knockoutjs.com/examples/