Chapter 2. Building HTML Templates
After we successfully installed Meteor and set up our folder structure, we can now start building the basic templates for our blog.
In this chapter, we will learn how templates are built. We will see how to display data and how some parts can be altered using helper functions. We will take a look on adding events, using conditions, and understanding data contexts, all in templates.
The following is an overview of what will be covered in this chapter:
- The basic template structure
- Displaying data
- Writing template helper functions
- Using conditions in templates
- Data contexts and how those can be set
- Nesting templates and data context inheritance
- Adding events
- Building block helpers
Note
If you jump right into this chapter without setting up the folder structure in the Chapter 1, Getting Started with Meteor, download the previous chapter's code examples from either the book's web page at https://www.packtpub.com/books/content/support/17713 or from the GitHub repository at https://github.com/frozeman/book-building-single-page-web-apps-with-meteor/tree/chapter1.
These code examples will also contain all the style files, so we don't have to worry about adding CSS code along the way.
Writing templates in Meteor
Normally when we build websites, we build the complete HTML on the server side. This was quite straightforward; every page is built on the server, then it is sent to the client, and at last JavaScript added some additional animation or dynamic behavior to it.
This is not so in single-page apps, where every page needs to be already in the client's browser so that it can be shown at will. Meteor solves this problem by providing templates that exists in JavaScript and can be placed in the DOM at some point. These templates can have nested templates, allowing for an easy way to reuse and structure an app's HTML layout.
Since Meteor is so flexible in terms of folder and file structure, any *.html
page can contain a template and will be parsed during Meteor's build process. This allows us to put all templates in the my-meteor-blog/client/templates
folder, which we created in the Chapter 1, Getting Started with Meteor. This folder structure is chosen as it helps us organizing templates when our app grows.
Meteor's template engine is called Spacebars, which is a derivative of the handlebars template engine. Spacebars is built on top of Blaze, which is Meteor's reactive DOM update engine.
Note
Blaze can generate reactive HTML directly using its API, though it's more convenient to use Meteor's Spacebars or a third-party template language built on top of Blaze such as Jade for Meteor.
For more detail about Blaze, visit https://docs.meteor.com/#/full/blaze and https://github.com/mquandalle/meteor-jade.
What makes Spacebars so exciting is its simplicity and reactivity. Reactive templates mean that some parts of the template can automatically change when the underlying data changes. There is no need of manual DOM manipulation and inconsistent interfaces belong to the past. To get a better understanding of Meteor, we will start with the basic HTML files for our app:
- Let's create an
index.html
file in ourmy-meteor-blog/client
folder with the following lines of code:<head> <title>My Meteor Blog</title> </head> <body> Hello World </body>
Note
Note that our
index.html
file doesn't contain the<html>...</html>
tags, as Meteor gathers all<head>
and<body>
tags in any file and builds up its ownindex.html
file, which will be delivered to the user. Actually, we can also name this filemyapp.html
. - Next, we run our Meteor app from the command line by typing the following command:
$ cd my-meteor-blog $ meteor
This will start a Meteor server with our app running.
- That's it! We can open our browser, navigate to
http://localhost:3000
, and we should see Hello World.
What happens here is that Meteor will look through all the HTML files available in our app's folder, concatenating the content of all <head>
and <body>
tags, which it finds and serve them to the clients as its index file.
If we take a look at the source code of our app, we will see that the <body>
tag is empty. This is because Meteor sees the content of the <body>
tag as its own templates, which will be injected with its corresponding JavaScript template when the DOM is loaded.
Note
To see the source code, don't use the Developer Tools' elements panel, as this will show us the source code after the JavaScript is executed. Right-click on the website instead and select View page source in Chrome.
We will also see that Meteor already linked all kinds of JavaScript files in our <head>
tag. These are Meteor's core packages and our add third-party packages. In production, these files will be concatenated into one. To see this in action, go to the terminal, quit our running Meteor server using Ctrl + C, and run the following command:
$ meteor --production
If we now take a look at the source code, we will see that there is only one cryptic-looking JavaScript file linked.
For the next steps, it is better to go back to our developer mode by simply quitting Meteor and running the meteor
command again, since this will reload the app faster when file changes occur.
Building the basic templates
Now, let's add the basic templates to our blog by creating a file called layout.html
in the my-meteor-blog/client/templates
folder. This template will serve as the wrapper template for our blog layout. To build the basic templates, perform the following steps:
- Add the following lines of code to
layout.html
, which we just created:<template name="layout"> <header> <div class="container"> <h1>My Meteor Single Page App</h1> <ul> <li> <a href="/">Home</a> </li> <li> <a href="/about">About</a> </li> </ul> </div> </header> <div class="container"> <main> </main> </div> </template>
- Next, we will create the home page template, which will later list all our blogs posts. In the same templates folder as
layout.html
, we will create a file namedhome.html
with the following lines of code:<template name="home"> {{#markdown}} ## Welcome to my Blog Here I'm talking about my latest discoveries from the world of JavaScript. {{/markdown}} </template>
- The next file will be a simple About page and we save it as
about.html
with the following code snippet:<template name="about"> {{#markdown}} ## About me Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud **exercitation ullamco** laboris nisi ut aliquip ex ea commodo consequat. Link to my facebook: [facebook.com][1] [1]: http://facebook.com {{/markdown}} </template>
As you can see, we used a
{{#markdown}}
block helper to wrap our texts. The curly braces are handlebars syntax, which Blaze uses to bring logic to the HTML. The{{#markdown}}...{{/markdown}}
block will transform all markdown syntax inside into HTML when the template gets rendered.Note
The markdown text cannot be indented as we do with the HTML tags because the markdown syntax interprets indentation as code.
- To be able to use
{{#markdown}}
block helper, we need to first add themarkdown
core package to our app. To do this, we quit our running app in the terminal using Ctrl + C and type the following command:$ meteor add markdown
- Now we can run the
meteor
command again to start our server.
However, when we now go to our browser, we will still see Hello World. So how can we make now our templates visible?
Adding templates and partials
To show the home template in the app, we need to open index.html
, which we created earlier, and perform the following steps:
- We replace
Hello World
with the following template inclusion helper:{{> layout}}
- If we go back to our browser now, we see that the text is gone and the
layout
template, which we created earlier, has appeared with its header and menu. - To complete the page, we need to show the
home
template in thelayout
template. We do this by simply adding another template inclusion helper to themain
section of thelayout
template in ourlayout.html
file, as follows:<main> {{> home}} </main>
- If we go back to the browser, we should see the following screenshot:
If we would now switch {{> home}}
for {{> about}}
, we would see our about
template instead.
Displaying data with template helpers
Each template can have functions, which are called template
helpers, and they can be used inside the template and child templates.
In addition to our custom helper functions, there are three callback functions that are called when the template is created, rendered, and destroyed. To display data with template helpers, perform the following steps:
- To see the three callback functions in action, let's create a file called
home.js
and save it to ourmy-meteor-blog/client/templates/
folder with the following code snippet:Template.home.created = function(){ console.log('Created the home template'); }; Template.home.rendered = function(){ console.log('Rendered the home template'); }; Template.home.destroyed = function(){ console.log('Destroyed the home template'); };
If we now open the console of our browser, we will see the first two callbacks are being fired. The last one will only fire if we dynamically remove the template.
- To display data in the
home
template, we will create a helper function that will return a simple string as follows:Template.home.helpers({ exampleHelper: function(){ return 'This text came from a helper with some <strong>HTML</strong>.'; } });
- Now if we go to our
home.html
file, add the{{exampleHelper}}
helper after the{{markdown}}
block helper, and save the file, we will see the string appearing in our browser, but we will notice that the HTML is escaped. - To make Meteor render the HTML correctly, we can simply replace the double curly braces with triple curly braces, as shown in the following line of code, and Blaze won't let the HTML escape:
{{{exampleHelper}}}
Note
Note that in most of our templates helper, we shouldn't use triple stache
{{{...}}}
as this opens the door for XSS and other attacks. Only use it if the HTML returned is safe to be rendered. - Additionally, we can return unescaped HTML using double curly braces, but we need to return the string passed through the
SpaceBars.SafeString
function, as shown in the following example:Template.home.helpers({ exampleHelper: function(){ return new Spacebars.SafeString('This text came from a helper with some <strong>HTML</strong>.'); } });
Setting the data context for a template
Now that we've seen how we can display data using a helper, let's see how we can set the whole data context of a template:
- For the next examples, we will create a file called
examples.html
in ourmy-meteor-blog/client/templates
folder and add the following code snippet:<template name="contextExample"> <p>{{someText}}</p> </template>
- Now that we have our
contextExample
template, we can add it to ourhome
template by passing some data as follows:{{> contextExample someText="I was set in the parent template's helper, as an argument."}}
This will show the text in the
contextExample
template because we were displaying it using{{someText}}
.Tip
Remember that filenames don't really matter as Meteor is collecting and concatenating them anyway; however, the template name matters since we use this to reference templates.
Setting the context in HTML is not very dynamic, as it is hardcoded. To be able to dynamically change the context, it is better to set it using a
template
helper function. - To do this, we must first add the helper to our
home
templates helpers, which returns the data context, as follows:Template.home.helpers({ // other helpers ... dataContextHelper: function(){ return { someText: 'This text was set using a helper of the parent template.', someNested: { text: 'That comes from "someNested.text"' } }; } });
- Now we can add this helper as the data context to our
contextExample
template inclusion helper, as follows:{{> contextExample dataContextHelper}}
- Also, to show the nested data object we return, we can use Blaze dot syntax in the
contextExample
template by adding the following line of code to the template:<p>{{someNested.text}}</p>
This will now display both the someText
and the someNested.text
, which was returned by our helper functions.
Using the {{#with}} block helper
Another way of setting the data context is by using the {{#with}}
block helper. The following code snippet has the same result as the former inclusion helper that utilizes the helper function:
{{#with dataContextHelper}} {{> contextExample}} {{/with}}
We would even get the same results in the browser when we don't use a subtemplate and just add the content of the contextExample
template inside the {{#with}}
block helper, as follows:
{{#with dataContextHelper}} <p>{{someText}}</p> <p>{{someNested.text}}</p> {{/with}}
Using the {{#with}} block helper
Another way of setting the data context is by using the {{#with}}
block helper. The following code snippet has the same result as the former inclusion helper that utilizes the helper function:
{{#with dataContextHelper}} {{> contextExample}} {{/with}}
We would even get the same results in the browser when we don't use a subtemplate and just add the content of the contextExample
template inside the {{#with}}
block helper, as follows:
{{#with dataContextHelper}} <p>{{someText}}</p> <p>{{someNested.text}}</p> {{/with}}
"this" in template helpers and template callbacks
In Meteor, this
in template helpers is used differently in template callbacks such as created()
, rendered()
, and destroyed()
.
As already mentioned, templates have three callback functions that are fired in different states of the template:
created
: This fires when the template gets initiated but is not yet in the DOMrendered
: This fires when the template and all its sub templates are attached to the DOMdestroyed
: This fires when the template is removed from the DOM and before the instance of the template gets destroyed
In these callback functions, this
refers to the current template instance. The instance object can access the templates DOM and comes with the following methods:
this.$(selectorString)
: This method finds all elements that matchselectorString
and returns a jQuery object from those elements.this.findAll(selectorString)
: This method finds all elements that matchselectorString
, but returns the plain DOM elements.this.find(selectorString)
: This method finds the first element that matchesselectorString
and returns a plain DOM element.this.firstNode
: This object contains the first element in the template.this.lastNode
: This object contains the last element in the template.this.data
: This object contains the templates data contextthis.autorun(runFunc)
: A reactiveTracker.autorun()
function that is stopped when the template instance is destroyed.this.view
: This object contains theBlaze.View
instance for this template.Blaze.View
are the building blocks of reactive templates.
Inside helper functions, this
refers only to the current data context.
To make these different behaviors visible, we will take a look at some examples:
- When we want to access the DOM of a template, we must do it in the rendered callback because only at this point, the template elements will be in the DOM. To see it in action, we edit our
home.js
file as follows:Template.home.rendered = function(){ console.log('Rendered the home template'); this.find('p').innerHTML = 'We just replaced that text!'; };
This will replace the first
p
tag that is created by the{{#markdown}}
block helper, which we put there before, with the string we set. Now when we check the browser, we will see that the first<p>
tag that contained our blog's introduction text has been replaced. - For the next example, we need to create an additional template JavaScript file for our
contextExample
template. To do this, we create a new file calledexamples.js
in ourtemplates
folder and save it using the following code snippet:Template.contextExample.rendered = function(){ console.log('Rendered Context Example', this.data); }; Template.contextExample.helpers({ logContext: function(){ console.log('Context Log Helper', this); } });
This will add the rendered callback as well as a helper called
logContext
to ourcontextExample
template helpers. To make this helper run, we also need to add this helper to ourcontextExample
template as follows:<p>{{logContext}}</p>
When we now go back to the console of our browser, we see that the data context object has been returned for all the rendered
callbacks and helpers from our rendered contextTemplates
template. We can also see that helpers will run before the rendered callback.
Note
In case you need access to the templates instance from inside a template helper, you can use Template.instance()
to get it.
Now let's use make our template interactive using events.
Adding events
To make our template a bit more dynamic, we will add a simple event, which will reactively rerun the logContext
helper we created earlier.
First, however, we need to add a button to our contextExample
template:
<button>Get some random number</button>
To catch the click event, open examples.js
and add the following event
function:
Template.contextExample.events({ 'click button': function(e, template){ Session.set('randomNumber', Math.random(0,99)); } });
This will set a session variable called randomNumber
to a random number.
Note
We will talk in depth about sessions in the next chapter. For now, we only need to know that when a session variable changes, all functions that get that session variable using Session.get('myVariable')
will run again.
To see this in action, we will add a Session.get()
call to the logContext
helper, and return the former set's random number as follows:
Template.contextExample.helpers({
logContext: function(){
console.log('Context Log Helper',this);
return Session.get('randomNumber');
}
});
If we go to the browser, we will see the Get some random number button. When we click on it, we see a random number appearing just above the button.
Note
When we use the contextTemplates
template multiple times in our home
template, we will see that each instance of that template helper will display the same random number. This is because the session object will rerun all its dependencies, all of which are instances of the logHelper
helper.
Now that we have covered template helpers, let's create a custom block helper.
Block helpers
Block helpers are templates that wrap the content of the block. They can be used to show content in different ways depending on conditions, or they can be used to add extra functionality to the blocks content, for example, some JavaScript calculation on its DOM elements.
In the following example, we will build a simple block helper that will show content based on a Boolean condition.
To do this, we will to add the following code snippet at the end of our example.html
file:
<template name="blockHelperExample"> <div> <h1>My Block Helper</h1> {{#if this}} <p>Content goes here: {{> Template.contentBlock}}</p> {{else}} <p>Else content here: {{> Template.elseBlock}}</p> {{/if}} </div> </template>
The {{> Template.contentBlock}}
is a predefined placeholder for the block's content. The same applies for {{> Template.elseBlock}}
.
When this
(in this example, we use the template's context as a simple Boolean) is true
, it will show the given Template.contentBlock
. Otherwise, it will show the Template.elseBlock
content.
To see how we can use the recently created template as a block helper, take a look at the following example, which we can add to home
template:
{{#blockHelperExample true}} <span>Some Content</span> {{else}} <span>Some Warning</span> {{/blockHelperExample}}
Now we should see the following screenshot:
When we now change true
, which we pass to {{#blockHelperExample}}
, to false
, we should see the content after the {{else}}
instead.
We can also use a helper function to replace the Boolean value, so that we can switch the block helper dynamically. Additionally, we can pass key-value arguments and access them by their key inside the block helper template, as shown in the following code example:
{{#blockHelperExample myValue=true}} ... {{/blockHelperExample}}
We can also access the given argument by its name in the block template as follows:
<template name="blockHelperExample">
<div>
<h1>My Block Helper</h1>
{{#if myValue}}
...
{{/if}}
</div>
</template>
Note
Note that the data context for the block's content will be the one from the template in which the block appears, not the one of the block helper template itself.
Block helpers are a powerful tool because they allow us to write self-contained components that, when packed into a package, can be used as a drop-in-place functionality by others. This feature has the potential to allow for a vibrant marketplace, like the marketplace we see in jQuery plugins.
Listing posts
Now that we have walked through all ways of using helpers and data, I want to introduce a block helper named {{#each}}
, which we will probably find the most useful.
If we go through all the examples completed up to now, we can see that it is better to delete all the examples of data context from our home
template, the examples.html
file, and its examples.js
JavaScript file so that we can continue to build our blog cleanly.
The next step is to add a list of blog entries to our home page. For this, we need to create a template for a post preview. This can be done by performing the following steps:
- We create a file called
postInList.html
in ourmy-meteor-blog/client/templates
folder and save it with the following code snippet:<template name="postInList"> <div class="postListItem"> <h2><a href="#">{{title}}</a></h2> <p>{{description}}</p> <div class="footer"> Posted by {{author}} </div> </div> </template>
This template will be used for each post we display in the home page.
- To make it appear, we need to add a
{{#each}}
helper to thehome
template, as follows:{{#each postsList}} {{> postInList}} {{/each}}
When the
postsList
helper, which we pass to the{{#each}}
block helper, returns an array, the content of{{#each}}
will be repeated for each item in the array, setting the array item as the data context. - To see this in action, we add the
postsList
helper in ourhome.js
file to the template helpers, as follows:Template.home.helpers({ // other helpers ... postsList: function(){ return [ { title: 'My Second entry', description: 'Borem sodum color sit amet, consetetur sadipscing elitr.', author: 'Fabian Vogelsteller', timeCreated: moment().subtract(3, 'days').unix() }, { title: 'My First entry', description: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr.', author: 'Fabian Vogelsteller', timeCreated: moment().subtract(7, 'days').unix() } ]; } });
- As we can see, we return an array where each item is an object containing our post's data context. For
timeCreated
, we use themoment
function of our previously added third-party package. This will generate dummy timestamps of a few days in the past. If we now go to our browser, we will see the two posts listed, as shown in the following screenshot: - To display
timeCreated
from our post item in the correct format, we need to create a helper function to format the timestamp. However, because we want to use this helper in other templates later, we need to make it a global helper that can be accessed by any template. To do this, we create a file namedtemplate-helpers.js
and save it to ourmy-meteor-blog/client
folder, as it doesn't belonging to any specific template. - To register a global helper, we can use Meteor's
Template.registerHelper
function:Template.registerHelper('formatTime', function(time, type){ switch(type){ case 'fromNow': return moment.unix(time).fromNow(); case 'iso': return moment.unix(time).toISOString(); default: return moment.unix(time).format('LLLL'); } });
- Now, we only have to add the helper to our
postInList
template by replacing the content of the footer with the following code snippet:<div class="footer"> <time datetime="{{formatTime timeCreated "iso"}}">Posted {{formatTime timeCreated "fromNow"}} by {{author}}</time> </div>
Now, if we save both the files and go back to our browser, we will see a relative date added to our blog post's footer. This works because we pass the time and a type string to the helper, as follows:
{{formatTime timeCreated "fromNow"}}
The helper then returns the formatted date using a moment
function.
With this global helper, we can now format any Unix timestamp, in any template to relative times, ISO time strings, and a standard date format (using the LLLL format, which converts to Thursday, September 4, 1986, 8:30 P.M.).
Now that we have already used the {{#with}}
and {{#each}}
block helpers, let's take a look at the other default helpers and syntax that Blaze uses.
Spacebars syntax
To wrap it all up, lets summarize the Spacebars syntax:
Helper |
Description |
---|---|
|
The template helper can be a property from the template's data context or a template helper function. If a helper function and a property with the same name exist, the template helper will use the helper function instead. |
|
The inclusion helper is for a template and always expects a template object or null. |
|
With the |
...
|
A block helper that contains both HTML and the Spacebars syntax. |
By default, Spacebars comes with the following four default block helpers:
{{#if}}..{{/if}}
{{#unless}}..{{/unless}}
{{#with}}..{{/with}}
{{#each}}..{{/each}}
The
{{#if}}
block helper allows us to create simple conditions, as follows:
{{#if myHelperWhichReturnsABoolean}} <h1>Show me this</h1> {{else}} <strong>If not<strong> show this. {{/if}}
The
{{#unless}}
block helper works the same as {{#if}}
, but with swapped logic.
The
{{#with}}
block, as seen earlier, will set a new data context to its content and containing templates, and the {{#each}}
block helper will render multiple times, setting a different data context for each iteration.
Accessing parent data contexts
To complete our journey through the Spacebars syntax, let's take a closer look at the template helper syntax that we used to display data. As we've already seen, we can display data using the double curly braces syntax, as follows:
{{myData}}
Inside this helper, we can access the properties of an object using the dot syntax:
{{myObject.myString}}
We can also access a parent data context using a path-like syntax:
{{../myParentsTemplateProperty}}
Additionally, we can move more than just one context up:
{{../../someParentProperty}}
This feature allows us to be very flexible about the data context.
Note
If we want to do the same from inside a template helper, we can use the Template API Template.parentData(n
), where n
is the number of steps up to access the data context of parent templates.
Template.parentData(0)
is the same as Template.currentData()
, or this
if we are in a template helper.
Passing data to helpers
Passing data to helpers can be done in two different ways. We can pass arguments to a helper as follows:
{{myHelper "A String" aContextProperty}}
Then, we can access it in the helper as follows:
Template.myTemplate.helpers({ myHelper: function(myString, myObject){ // And we get: // myString = 'aString' // myObject = aContextProperty } });
Besides this, we can pass data in the form of key-values:
{{myHelper myString="A String" myObject=aDataProperty}}
This time, however, we need to access them as follows:
Template.myTemplate.helpers({ myHelper: function(Parameters){ // And we can access them: // Parameters.hash.myString = 'aString' // Parameters.hash.myObject = aDataProperty } });
Be aware that block and inclusion helpers act differently because they always expect objects or key-values as arguments:
{{> myTemplate someString="I will be available inside the template"}} // Or {{> myTemplate objectWithData}}
If we want to pass only a single variable or value to an inclusion or block helper, Meteor would objectify the argument, as we can see with the following code snippet:
{{#myBlock "someString"}} ... {{/myBlock}}
We would then need to typecast the passed argument if we want to use it in a helper function as follows:
Template.myBlock.helpers({ doSomethingWithTheString: function(){ // Use String(this), to get the string return this; } });
Beisdes, we can also simply display the string in our block helper template using {{Template.contentBlock}}
as follows:
<template name="myBlock"> <h1>{{this}}</h1> {{Template.contentBlock}} </template>
We can also pass another template helper as an argument to an inclusion or block helper, as shown in the following example:
{{> myTemplate myHelperWhichReturnsAnObject "we pass a string and a number" 300}}
Though passing data to template helpers and inclusion/block helpers are slightly different, arguments can be quite flexible when using helpers to generate them.
Accessing parent data contexts
To complete our journey through the Spacebars syntax, let's take a closer look at the template helper syntax that we used to display data. As we've already seen, we can display data using the double curly braces syntax, as follows:
{{myData}}
Inside this helper, we can access the properties of an object using the dot syntax:
{{myObject.myString}}
We can also access a parent data context using a path-like syntax:
{{../myParentsTemplateProperty}}
Additionally, we can move more than just one context up:
{{../../someParentProperty}}
This feature allows us to be very flexible about the data context.
Note
If we want to do the same from inside a template helper, we can use the Template API Template.parentData(n
), where n
is the number of steps up to access the data context of parent templates.
Template.parentData(0)
is the same as Template.currentData()
, or this
if we are in a template helper.
Passing data to helpers
Passing data to helpers can be done in two different ways. We can pass arguments to a helper as follows:
{{myHelper "A String" aContextProperty}}
Then, we can access it in the helper as follows:
Template.myTemplate.helpers({ myHelper: function(myString, myObject){ // And we get: // myString = 'aString' // myObject = aContextProperty } });
Besides this, we can pass data in the form of key-values:
{{myHelper myString="A String" myObject=aDataProperty}}
This time, however, we need to access them as follows:
Template.myTemplate.helpers({ myHelper: function(Parameters){ // And we can access them: // Parameters.hash.myString = 'aString' // Parameters.hash.myObject = aDataProperty } });
Be aware that block and inclusion helpers act differently because they always expect objects or key-values as arguments:
{{> myTemplate someString="I will be available inside the template"}} // Or {{> myTemplate objectWithData}}
If we want to pass only a single variable or value to an inclusion or block helper, Meteor would objectify the argument, as we can see with the following code snippet:
{{#myBlock "someString"}} ... {{/myBlock}}
We would then need to typecast the passed argument if we want to use it in a helper function as follows:
Template.myBlock.helpers({ doSomethingWithTheString: function(){ // Use String(this), to get the string return this; } });
Beisdes, we can also simply display the string in our block helper template using {{Template.contentBlock}}
as follows:
<template name="myBlock"> <h1>{{this}}</h1> {{Template.contentBlock}} </template>
We can also pass another template helper as an argument to an inclusion or block helper, as shown in the following example:
{{> myTemplate myHelperWhichReturnsAnObject "we pass a string and a number" 300}}
Though passing data to template helpers and inclusion/block helpers are slightly different, arguments can be quite flexible when using helpers to generate them.
Passing data to helpers
Passing data to helpers can be done in two different ways. We can pass arguments to a helper as follows:
{{myHelper "A String" aContextProperty}}
Then, we can access it in the helper as follows:
Template.myTemplate.helpers({ myHelper: function(myString, myObject){ // And we get: // myString = 'aString' // myObject = aContextProperty } });
Besides this, we can pass data in the form of key-values:
{{myHelper myString="A String" myObject=aDataProperty}}
This time, however, we need to access them as follows:
Template.myTemplate.helpers({ myHelper: function(Parameters){ // And we can access them: // Parameters.hash.myString = 'aString' // Parameters.hash.myObject = aDataProperty } });
Be aware that block and inclusion helpers act differently because they always expect objects or key-values as arguments:
{{> myTemplate someString="I will be available inside the template"}} // Or {{> myTemplate objectWithData}}
If we want to pass only a single variable or value to an inclusion or block helper, Meteor would objectify the argument, as we can see with the following code snippet:
{{#myBlock "someString"}} ... {{/myBlock}}
We would then need to typecast the passed argument if we want to use it in a helper function as follows:
Template.myBlock.helpers({ doSomethingWithTheString: function(){ // Use String(this), to get the string return this; } });
Beisdes, we can also simply display the string in our block helper template using {{Template.contentBlock}}
as follows:
<template name="myBlock"> <h1>{{this}}</h1> {{Template.contentBlock}} </template>
We can also pass another template helper as an argument to an inclusion or block helper, as shown in the following example:
{{> myTemplate myHelperWhichReturnsAnObject "we pass a string and a number" 300}}
Though passing data to template helpers and inclusion/block helpers are slightly different, arguments can be quite flexible when using helpers to generate them.
Summary
Reactive templates are one of the most impressive features of Meteor, and once we get used to them, we probably won't look back to manual DOM manipulation anymore.
After reading this chapter, we should know how to write and use templates in Meteor. We should also understand its basic syntax and how to add templates.
We saw how to access and set data in templates and how to use helpers. We learned about different types of helpers, such as inclusion helpers and block helpers. We also built our own custom block helpers and used Meteor's default helpers.
We learned that templates have three different callbacks, for when the template gets created, rendered, and destroyed.
We learned how to pass data to helpers, and how this differs in normal helpers and block helpers.
To dig deeper, take a look at the following documentations:
You can find this chapter's code examples either at https://www.packtpub.com/books/content/support/17713 or on GitHub at https://github.com/frozeman/book-building-single-page-web-apps-with-meteor/tree/chapter2.
With all this new knowledge about templates, we are ready to add data to our database and see how we can display it in our home page.