If Binding Magic! Avoiding Duplication Problem in Header Element IDs

November 29th, 2014 — 6 min read

If Binding Magic! Avoiding Duplication Problem in Header Element IDs

Welcome again. As you may already have seen my post regarding creating Sinlge Page Application using knockoutjs and jquery mobile.
One thing i wanted to discuss here that you might encounter a simple problem of duplicating the id attribute in header elements.
Why that can happen? Because you are using a template for header and this template is being injected in each page/section. Of
course the ids of header elements will be duplicated and you wont be able to make jquery work using ids and won’t be able to get
the item selected you want on some particular page.
I have seen many questions from people asking how this can be solved. So i thought i should present a simple solution.

Problem:
To understand the problem, consider this example. Here is a template

<nav class="v2_n">
	<a href="#home" id="home" data-transition="fade" >Home</a>
	<a href="#list" id="list" data-transition="fade" >List</a>
	<a href="#form" id="form" data-transition="fade" >Form</a>
</nav>
 

This template is being called on each section/page.

<section id="home" data-role="page"  data-bind="with:HomePage">
	<header>
		<nav class="v2_n">
			<a href="#home" id="home" data-transition="fade" >Home</a>
			<a href="#list" id="list" data-transition="fade" >List</a>
			<a href="#form" id="form" data-transition="fade" >Form</a>
		</nav>
	</header>
</section>
<section id="list" data-role="page"  data-bind="with:ListPage">
	<header>
		<nav class="v2_n">
			<a href="#home" id="home" data-transition="fade" >Home</a>
			<a href="#list" id="list" data-transition="fade" >List</a>
			<a href="#form" id="form" data-transition="fade" >Form</a>
		</nav>
	</header>
</section>
<section id="form" data-role="page"  data-bind="with:FormPage">
	<header>
		<nav class="v2_n">
			<a href="#home" id="home" data-transition="fade" >Home</a>
			<a href="#list" id="list" data-transition="fade" >List</a>
			<a href="#form" id="form" data-transition="fade" >Form</a>
		</nav>
	</header>
</section>

Solution:

While working on SPA using this approach it is very simple to avoid this problem. You just need a property in parent view model. Let see how we can do this

function VM() {
	var self = this;
 
	self.Title = ko.observable('Knockout! Parent and Child Usage Techniques')
	self.Home  = ko.observable()
	self.Form  = ko.observable()
	self.List  = ko.observable()
 
	/*	Define a property to hold id of current page	*/
	self.CurrentPage = ko.observable()
 
	.
	.
	.
}

And on page change event use this

$(window).on( "pagechange", function( event, data ) {
	var page_id	=	data.toPage[0].id
	/*	
		While navigating among pages each time pass 
		the page id to CurrentPage property	
	*/		
	vm.CurrentPage(page)
 
	switch(page_id)
	{
		case 'form':
		    vm.FormPage().LoadData()
			break;						
		case 'list':
			vm.ListPage().LoadData()	
			break;						
		default:
			vm.HomePage().LoadData()
			break;
	}
});

And now on view you need a simple condition

<section id="home" data-role="page"  data-bind="with:HomePage">
	<!-- ko if:$root.CurrentPage() == 'home' -->	
	<header>
		<nav class="v2_n">
			<a href="#home" id="home" data-transition="fade" >Home</a>
			<a href="#list" id="list" data-transition="fade" >List</a>
			<a href="#form" id="form" data-transition="fade" >Form</a>
		</nav>
	</header>
	<section class="main_body max_600width">
		// Other content				
	</section>			
	<!-- /ko -->
</section>
<section id="list" data-role="page"  data-bind="with:ListPage">
	<!-- ko if:$root.CurrentPage() == 'list' -->	
	<header>	
		<nav class="v2_n">
			<a href="#home" id="home" data-transition="fade" >Home</a>
			<a href="#list" id="list" data-transition="fade" >List</a>
			<a href="#form" id="form" data-transition="fade" >Form</a>
		</nav>
	</header>
	<section class="main_body max_600width">
		// Other content				
	</section>	
	<!-- /ko -->			
</section>
<section id="form" data-role="page"  data-bind="with:FormPage">
	<!-- ko if:$root.CurrentPage() == 'form' -->	
	<header>	
		<nav class="v2_n">
			<a href="#home" id="home" data-transition="fade" >Home</a>
			<a href="#list" id="list" data-transition="fade" >List</a>
			<a href="#form" id="form" data-transition="fade" >Form</a>
		</nav>
	</header>
	<section class="main_body max_600width">
		// Other content				
	</section>	
	<!-- /ko -->			
</section>

Now what this does is when you navigate between pages the other contents in each sections gets removed and the page being viewed is generated. So if you inspect section elements you will see only current page is having content and all other sections are empty. This way you can avoid duplication of id attributes in header. Hope this simple technique is useful.

You can view a demo here. Any question, i am here to explain.