DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Building Scalable Real-Time Apps with AstraDB and Vaadin
Register Now

Trending

  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • Web Development Checklist
  • Building and Deploying Microservices With Spring Boot and Docker
  • Best Practices for Securing Infrastructure as Code (Iac) In the DevOps SDLC

Trending

  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • Web Development Checklist
  • Building and Deploying Microservices With Spring Boot and Docker
  • Best Practices for Securing Infrastructure as Code (Iac) In the DevOps SDLC

Demo of Handlebars, and Why You Should Consider a Templating Engine

Raymond Camden user avatar by
Raymond Camden
·
Apr. 22, 12 · Interview
Like (0)
Save
Tweet
Share
18.14K Views

Join the DZone community and get the full member experience.

Join For Free

for a while now i've been thinking i need to pick up, and start using, a javascript templating engine. i had used a jquery-based one a few years back, but that project was abandoned and i've yet to really look what - if any - solution would work good for me. another reason i've not found the time is that a majority of my javascript examples are small little demos built for blog posts. when i blog, i try my best to keep my code as simple as possible. i do go all mvc just to demonstrate date formatting. it may not be real world, but it also keeps you focused on the topic i'm trying to discuss.

today i made the time - and more specifically - made a demo. the demo is stupid. it's not even important. what is important is this:

if you've ever used javascript to build strings of html, you never realized just how much of a pain that is until you don't have to. you never realized how resistant you are to adding new features - or tweaking the design. you never realized how much you held back - just because of how much of a pain in the rear it was!

i'm probably being overly dramatic, but to me, it feels a lot like orm. yeah, it's simple to go into a database client, open a table, and add a new field. but when you can all of that via code... it feel incredibly freeing. you feel yourself trying new and interesting things. in fact, the demo i'm going to show has about twice the features i was planning just because it was so damn easy to add.

that's how i feel today - and any day where my computer makes me smile is a good day. ok, enough rambling.

i had heard about handlebars from various people. it's also the templating engine that ember.js uses. handlebars works by allowing you to define templates using simple script blocks, so for example, you can write your template in your document like so:

<script id="result-template" type="text/x-handlebars-template">
<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>
</script>

you then use the handlerbars api to create a template out of block, apply data to it, and then render it to screen. it's all relatively simple, but the docs don't necessarily do a great job i think of demonstrating simple examples in "full" pages so you can see things in context. here is a trivial example:

<!doctype html>
<html>
<head>
	<title>test 1</title>
	<script src="js/handlebars-1.0.0.beta.6.js"></script>
	<script id="result-template" type="text/x-handlebars-template">
	<h2>your bio</h2>
	<p>
	your name is {{firstname}} {{lastname}} and you are {{age}} years old.
	</p>
	</script>
    <link rel="stylesheet" href="style.css" type="text/css" />
</head>

<body>

	<h2>render simple bio</h2>

	<input type="text" id="firstname" placeholder="first name"><br/>
	<input type="text" id="lastname" placeholder="last name"><br/>
	<input type="number" id="age" placeholder="age"><br/>
	<button id="demobutton">demo</button>

	<div id="resultdiv"></div>

	<script>

	document.addeventlistener("domcontentloaded", function() {

		//get the contents from the script block 
		var source = document.queryselector("#result-template").innerhtml;
		//compile that baby into a template
		template = handlebars.compile(source);

		document.queryselector("#demobutton").addeventlistener("click", function() {
			var fname = document.queryselector("#firstname").value;
			var lname = document.queryselector("#lastname").value;
			var age = document.queryselector("#age").value;

			var html = template({firstname:fname, lastname:lname,age:age});
			document.queryselector("#resultdiv").innerhtml = html;

		});
	});


	</script>

</body>
</html>

notice how i've got a simple template block on top. if you've never seen handlebars before, or any javascript templating engine, you can probably guess which portions of the block represent dynamic portions and which represent static text.

i've got a simple form with a button bound to a simple click listener. looking at the javascript, you can see that first i have to grab the html from the template block. i then compile this. this gives me a template that i can reuse to generate output.

my form has a simple click handler. when you hit the button, i pass the values to my template and grab the html out of it. you can run this demo here:

http://raymondcamden.com/demos/2012/apr/19/test1.html

of course, not every template will be a simple set of keys and values. your template may also need to be dynamic based on the values passed in. let's look at another example that makes use of both lists and conditionals.

<!doctype html>
<html>
<head>
	<title>test 2</title>
	<script src="js/handlebars-1.0.0.beta.6.js"></script>
	<script id="result-template" type="text/x-handlebars-template">
	<h2>your favorite things</h2>

	{{#if things}}
		<ul>
		{{#each things}}
			<li>{{this}}</li>
		{{/each}}
		</ul>
	{{else}}

		<p>
		apparently, you like nothing. poor you.
		</p>

	{{/if}}
	</script>
    <link rel="stylesheet" href="style.css" type="text/css" />
</head>

<body>

	<h2>list of things</h2>
	<p>
		enter a comma-separated list of things you like.
	</p>
	<input type="text" id="things" placeholder="things you like..."><br/>
	<button id="demobutton">demo</button>

	<div id="resultdiv"></div>

	<script>

	document.addeventlistener("domcontentloaded", function() {

		//get the contents from the script block 
		var source = document.queryselector("#result-template").innerhtml;
		//compile that baby into a template
		template = handlebars.compile(source);

		document.queryselector("#demobutton").addeventlistener("click", function() {
			var things = document.queryselector("#things").value;
			if(things.length) var arrthings = things.split(",");

			var html = template({things:arrthings});
			document.queryselector("#resultdiv").innerhtml = html;

		});
	});


	</script>

</body>
</html>

in our template, we've got two things going on here. first is a conditional that checks if "things" is a truthy value (truthy being one of the things that make javascript so fun). within the true part of the conditional we use an each block to enumerate over a set of values.

if you scroll down to the html/javascript, you can see i'm just asking for you to enter a list of things you like. that value is split into an array and passed (if there were values) to the template. demo this below..

http://raymondcamden.com/demos/2012/apr/19/test2.html

let's look at one more example. one of the cooler aspects of handlebars is that you can add custom functions to the engine. for example, you could write a cowbell function that wraps your results in the beautiful rocking sounds of the cowbell. ok, maybe not that. but what about something a bit complex - like converting an email address into a md5 hash that could be used for gravatar? yeah - no way that would work...

<!doctype html>
<html>
<head>
	<title>test 3</title>
	<script src="js/handlebars-1.0.0.beta.6.js"></script>
	<script src="js/webtoolkit.md5.js"></script>
	<script id="result-template" type="text/x-handlebars-template">
	<h2>you and your gravatar</h2>
	<p>
	your email is {{email}} and your gravatar is:<br/>
	<img src="{{gravatarurl email }}">
	</p>

	</script>
    <link rel="stylesheet" href="style.css" type="text/css" />
</head>

<body>

	<h2>enter email address for awesomeness</h2>
	<input type="email" id="email" placeholder="email goes here..."> 
	<button id="demobutton">demo</button>

	<div id="resultdiv"></div>

	<script>

	document.addeventlistener("domcontentloaded", function() {

		//tip on using gravar with js: http://www.deluxeblogtips.com/2010/04/get-gravatar-using-only-javascript.html
		handlebars.registerhelper('gravatarurl', function(email) {
		    return 'http://www.gravatar.com/avatar/' + md5(email) + '.jpg?s=250';
		});

		//get the contents from the script block 
		var source = document.queryselector("#result-template").innerhtml;
		//compile that baby into a template
		template = handlebars.compile(source);

		document.queryselector("#demobutton").addeventlistener("click", function() {
			var email = document.queryselector("#email").value;
			if(!email.length) return;

			var html = template({email:email});
			document.queryselector("#resultdiv").innerhtml = html;

		});
	});


	</script>

</body>
</html>

notice in the template we have one simple value, email, and then this: gravatar email. this isn't something built into handlebars, but rather, injected via the registerhelper function you see in the main script block of the page. you can demo this here:

http://raymondcamden.com/demos/2012/apr/19/test3.html ok. time to kick it up a notch. many moons ago jason dean introduced me to the comicvine api. this is a free api that provides access to their pretty deep database of comic book data. unfortunately their api isn't very well supported and the documentation is missing a few important details. but i was able to take their service and build the following.

<!doctype html>
<html>
<head>
	<title>comic character searcher</title>
	<script src="js/handlebars-1.0.0.beta.6.js"></script>
	<script id="result-template" type="text/x-handlebars-template">
	<h2>results</h2>
	<p>
	your search returned {{number_of_total_results}} result(s).
	</p>

	{{#each results}}
	<div class="result">
	<h3>{{name}}</h3>
	<p>{{{lefttrim description}}}</p>
	<a href="{{site_detail_url}}">
	{{#if image.super_url}}
	<img src="{{image.super_url}}">
	{{else}}
	<img src="generic.png">
	{{/if}}
	</a>
	</div>
	{{/each}}
	</script>
    <link rel="stylesheet" href="style.css" type="text/css" />
</head>

<body>

	comic character search: 
	<input type="text" id="search" autofocus> <i>results credit comicvine.com</i>

	<div id="resultdiv"></div>

	<script>
	var baseurl = "http://api.comicvine.com/search/?api_key=4da2671a38f182f28110923ac684980d9658628a&format=jsonp&json_callback=handleresponse&resources=character&field_list=image,site_detail_url,name,description";

	var template;
	var resultdiv = document.queryselector("#resultdiv");

	//custom helper to trim and remove html as well
	handlebars.registerhelper('lefttrim', function(text) {
		text = text.replace(/<.*?>/g," ").trim();
		if(text.length > 100) return text.substring(0,100) +"...";
		else return text;
	});

	document.addeventlistener("domcontentloaded", function() {
		console.log("business time");

		var source = document.queryselector("#result-template").innerhtml;
		template = handlebars.compile(source);

		document.queryselector("#search").addeventlistener("keyup", function() {
			var text = this.value.trim();
			if(text.length <= 3) return;

			var requrl = baseurl + "&query="+escape(text);
			//credit: http://stackoverflow.com/a/9649610/52160
			var script = document.createelement('script');
			script.src = requrl;
			document.getelementsbytagname('head')[0].appendchild(script);

		});
	});

	function handleresponse(resp) {
		//console.dir(resp);
		if(resp.error && resp.error == "ok") {
			var html = template(resp);
			resultdiv.innerhtml = html;
		}
	}
	</script>

</body>
</html>

this application uses json/p and the comicvine api to let you search against their character database. a good search string is "spider". obviously it doesn't have everything in there, but it's fun to see what's there. oh yeah - be sure to search for "beyonder" - the best thing to come out of marvel in the 80s. you can demo this here:

Engine Template

Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • Web Development Checklist
  • Building and Deploying Microservices With Spring Boot and Docker
  • Best Practices for Securing Infrastructure as Code (Iac) In the DevOps SDLC

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: