Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Meteor: Full-Stack Web Application Development

You're reading from   Meteor: Full-Stack Web Application Development Rapidly build web apps with Meteor

Arrow left icon
Product type Course
Published in Nov 2016
Publisher Packt
ISBN-13 9781787287754
Length 685 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Fabian Vogelsteller Fabian Vogelsteller
Author Profile Icon Fabian Vogelsteller
Fabian Vogelsteller
Marcelo Reyna Marcelo Reyna
Author Profile Icon Marcelo Reyna
Marcelo Reyna
Arrow right icon
View More author details
Toc

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:

  1. Let's create an index.html file in our my-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 own index.html file, which will be delivered to the user. Actually, we can also name this file myapp.html.

  2. 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.

  3. 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:

  1. 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>
  2. 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 named home.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>
  3. 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.

  4. To be able to use {{#markdown}} block helper, we need to first add the markdown 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
    
  5. 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:

  1. We replace Hello World with the following template inclusion helper:
    {{> layout}}
  2. 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.
  3. To complete the page, we need to show the home template in the layout template. We do this by simply adding another template inclusion helper to the main section of the layout template in our layout.html file, as follows:
    <main>
      {{> home}}
    </main>
  4. If we go back to the browser, we should see the following screenshot:
    Adding templates and partials

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:

  1. To see the three callback functions in action, let's create a file called home.js and save it to our my-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.

  2. 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>.';
      }
    });
  3. 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.
  4. 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.

  5. 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:

  1. For the next examples, we will create a file called examples.html in our my-meteor-blog/client/templates folder and add the following code snippet:
    <template name="contextExample">
      <p>{{someText}}</p>
    </template>
  2. Now that we have our contextExample template, we can add it to our home 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.

  3. 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"'
          }
        };
      }
    });
  4. Now we can add this helper as the data context to our contextExample template inclusion helper, as follows:
    {{> contextExample dataContextHelper}}
  5. 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 DOM
  • rendered: This fires when the template and all its sub templates are attached to the DOM
  • destroyed: 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 match selectorString and returns a jQuery object from those elements.
  • this.findAll(selectorString): This method finds all elements that match selectorString, but returns the plain DOM elements.
  • this.find(selectorString): This method finds the first element that matches selectorString 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 context
  • this.autorun(runFunc): A reactive Tracker.autorun() function that is stopped when the template instance is destroyed.
  • this.view: This object contains the Blaze.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 called examples.js in our templates 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 our contextExample template helpers. To make this helper run, we also need to add this helper to our contextExample 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:

Block helpers

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:

  1. We create a file called postInList.html in our my-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.

  2. To make it appear, we need to add a {{#each}} helper to the home 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.

  3. To see this in action, we add the postsList helper in our home.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()
          }
        ];
      }
    });
  4. As we can see, we return an array where each item is an object containing our post's data context. For timeCreated, we use the moment 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:
    Listing posts
  5. 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 named template-helpers.js and save it to our my-meteor-blog/client folder, as it doesn't belonging to any specific template.
  6. 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');
      }
    });
  7. 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

{{myProperty}}

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.

{{> myTemplate}}

The inclusion helper is for a template and always expects a template object or null.

{{> Template.dynamic template=templateName [data=dataContext]}}

With the {{> Template.dynamic ...}} helper, you can render a template dynamically by providing a template helper returning a template name for the template parameter. When the helper would rerun and return a different template name, it will replace the template on this position with the new one.

{{#myBlockHelper}}

...

{{/myBlockHelper}}

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.

You have been reading a chapter from
Meteor: Full-Stack Web Application Development
Published in: Nov 2016
Publisher: Packt
ISBN-13: 9781787287754
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image