Chapter 5: Templates

Templates

In this chapter, we’re going to start creating the user interface for the Leaderboard application, and to do this, we’ll need to create our first templates.

Within Meteor, templates are used to create a connection between the project’s interface and the project’s JavaScript code. When we place interface elements inside a template, such as a button or a form, we’re able to reference those elements and build an interface that users can interact with.

To begin, place the following code inside the leaderboard.html file:

<head>
    <title>Leaderboard</title>
</head>
<body>
    <h1>Leaderboard</h1>
</body>

This is the basic structure for our project’s interface, but there’s nothing special about this code. It’s just standard HTML. There does, however, appear to be a few things missing:

  • We haven’t included the html tags.
  • We haven’t included any JavaScript files.
  • We haven’t included any CSS files.

But we haven’t manually included these things because Meteor takes care of these details for us. It automatically adds the html tags to either end of the file and it automatically includes any resources contained within the project’s folder – such as the JavaScript and CSS files.

This isn’t the most remarkable feature in the world, but one of Meteor’s core philosophies is developer happiness, so there’s plenty of time-saving features throughout the framework.

Create a Template

To create a template, add the following code to the very bottom of the HTML file, beneath the closing body tag:

<template name="leaderboard">
    Hello World
</template>

Here, we’re using this template tag to define a template, and the name attribute to distinguish between the templates we create. In this case, the name of the template is “leaderboard” and, in a moment, we’ll reference this name from inside the JavaScript file.

But if we save the file in its current state, the template won’t appear inside the browser.

Why?

By default, templates don’t appear inside the interface.

This might sound weird, but consider that, in some cases:

  • We might want a template to appear at certain times.
  • We might want a template to disappear at the certain times.
  • We might want a template to appear in multiple locations.

To account for these possibilities, we need to manually include templates inside the interface. This requires an extra step but allows for a lot of flexibility as the interface grows in complexity.

To include the “leaderboard” template inside the interface and have it appear inside the browser, place the following tag between the body tags inside the HTML file:

{{> leaderboard}}

Obviously, this isn’t HTML. Instead, the use of double-curly braces means this is the Spacebars syntax, and Spacebars is the syntax we use in our HTML when we want something dynamic to occur. It’s the syntax that bridges the gap between the interface and the application logic.

We’ll learn more about Spacebars throughout this book, so there’s no need to think too hard about it just yet, but it’s important to know that:

  1. All Spacebars tags use double-curly braces to distinguish themselves from the HTML.
  2. We only use the left-angled bracket when including a template.

Based on these changes, the HTML file should now resemble:

<head>
    <title>Leaderboard</title>
</head>
<body>
    <h1>Leaderboard</h1>
    {{> leaderboard}}
</body>

<template name="leaderboard">
    Hello World
</template>

And after saving the file, the “Hello World” text from inside the “leaderboard” template should appear inside the browser.

Client vs. Server

Before we continue, I want to demonstrate something. You don’t have to fully understand what we’re about to talk about, but do follow along by writing out all of the code yourself.

Inside the leaderboard.js file, create a console.log statement. This is the same statement we wrote before, and after saving the file, the “Hello world” message should appear inside the Console.

What I didn’t mention last time though is that, because of this statement, something else is also happening, because if switch away from the browser, we can see the “Hello world” message is now appearing inside the command line:

This is significant because we’ve written one line of code that’s being executed in two places. The code is running on the client, inside the browser, and on the server, where the application is hosted.

There’s a few reasons why this matters, but here’s one example:

Ever since we created the “PlayersList” collection, the following line of code has been running on both the client and the server:

PlayersList = new Mongo.Collection('players');

But the code doesn’t do the same thing in both places.

When the code is executed on the server, a collection is created inside the Mongo database. This is where the project’s data is stored. When the code runs from inside the user’s web browser though – on the client-side – a local copy of the collection is downloaded to the user’s computer.

Because of this, when the user is interacting with the database, they’re actually interacting with a local copy. This is partly why Meteor applications are real-time by default. Users can interact with the database on their local machine, which happens instantaneously, and changes to that data are then invisibly synced in the background with the database on the server.

At the same time though, we don’t always want our code to be running in two places at once.

If, for instance, we were to write code that only affects the interface of an application, it wouldn’t make sense for that code to run on the server. We’d only want it to run inside the browser.

To accomodate for this, we can use conditionals to control where code is executed. You’ll have a better idea of when to use these conditionals as we progress through the book, but again, just follow along for the moment by writing out all of the code.

Inside the JavaScript file, write a conditional that tests whether or not Meteor.isClient returns true:

if(Meteor.isClient){
    // this code only runs on the client
}

This conditional allows us to specifically execute code on the client. To demonstrate this, place a console.log statement inside the conditional:

if(Meteor.isClient){
    console.log("Hello client");
}

Switch to the browser and notice that the “Hello client” message appears in the Console but does not appear inside the command line:

This is because the code is only being executed inside the web browser.

To create the reverse effect. write a conditional that tests whether or not Meteor.isServer returns true:

if(Meteor.isServer){
    // this code only runs on the server
}

…and place a console.log statement inside this conditional:

if(Meteor.isServer){
    console.log("Hello server");
}

After saving the file, notice that the “Hello server” message appears inside the command line but does not appear inside the Console. This is because the code is only being executed on the server, where the application is hosted. It’s not being executed from inside the web browser.

But if you’re struggling to grasp the details, fear not. Just remember three things:

  1. A single line of code can run on both the client and the server.
  2. This code can behave differently depending on the environment.
  3. Sometimes we don’t want the code to run in both places.

The moments where we need to consider these details will become clear in the coming chapters.

For now, delete the console.log statements from the conditionals but leave the conditionals where they are. We’ll be using them soon.

Create a Helper

At this point, the “leaderboard” template only contains “Hello World” text, which isn’t too interesting. To fix this, we’re going to create a helper function, and helper functions are JavaScript functions that are attached to templates and allow us to execute code from inside the interface.

To begin, we’re going to take an old approach to creating helper functions.

This approach is deprecated, meaning it’s no longer officially supported, and by the time you’re reading these words, it may not work at all. But this approach is easier to teach and to understand, and it allows us to ease into the non-deprecated approach, which we’ll talk about in a minute.

Inside the JavaScript file, write the following code inside the isClient conditional:

Template.leaderboard.player

This is the deprecated syntax for creating a helper function, and we can break it down into three parts:

  1. The Template keyword searches through all of the templates in the Meteor project. At the moment, we only have one template in one file, but a complete project could have hundreds of templates across hundreds of files.

  2. The leaderboard keyword is a reference to the name of the template we created earlier. Every helper function must be attached to a template. In this case, the function is attached to the “leaderboard” template.

  3. The player keyword is the name we’re assigning to the function, and we’ll soon reference this name from inside the HTML file.

As for why we’re placing this code inside the isClient conditional, it’s because the code specifically affects the interface of the application. Running the code on the server would result in an error.

To attach code to this helper, associate it with a function:

Template.leaderboard.player = function(){
    // code goes here
}

The word “helper” might make this sound like a strange new concept, but we haven’t done anything fancy. We’ve created a function, named it “player”, and attached it to the “leaderboard” template.

To make this helper function do something, write a return statement inside of it:

Template.leaderboard.player = function(){
    return "Some other text";
}

Then remove the “Hello World” text from the “leaderboard” template and replace it with the following tag:

{{player}}

Here, we’re using a Spacebars tag, as evidenced by the use of double-curly braces. But notice that we’re not using the left-angled bracket, and that’s because we’re not including a template. Instead, we’re referencing the name of the “player” function that we created a moment ago.

Because of this, the text being returned by the function will appear inside the browser:

If the text doesn’t appear, fear not. This approach to creating helper functions might have been removed from Meteor, but now that we’ve talked about the old way of creating helper functions, we’re ready to talk about the new way.

Delete the helper function we’ve just created and replace it with the following code:

Template.leaderboard.helpers

Here, we’re using this Template keyword, which searches through the templates inside the project, and this Leaderboard keyword, which is a reference to the “leaderboard” template.

But what about this helpers keyword?

Are we creating a helper function named “helpers”?

Nope.

This helpers keyword is a special keyword that allows us to define multiple helper functions inside a single block of code.

Therefore, instead of creating individual helper functions, like we did before:

Template.leaderboard.player = function(){
    // code goes here
}

We can create a block of code that holds all of a template’s helper functions:

Template.leaderboard.helpers({
    // helper functions go here
});

The helpers themselves are then defined in the JSON format, with the name of the helper as the key and a function as the value.

For example, here’s how we’d recreate the “player” function:

Template.leaderboard.helpers({
    'player': function(){
        return "Some other text";
    }
});

By using commas, we can create multiple helper functions inside a single block of code:

Template.leaderboard.helpers({
    'player': function(){
        return "Some other text";
    },
    'otherHelperFunction': function(){
        return "Some other function";
    }
});

We can then reference these helper functions from inside the template:

{{player}}
{{otherHelperFunction}}

This code is a little more verbose than the deprecated approach, but that’s only because we’re working with a small number of helpers attached to a single template. As the project grows in complexity, the organization will make it easier to manage the helpers.

Each Blocks

The problem with the helper function we’ve created is that it just returns some static text, which isn’t that interesting. What we want is for the helper function to retrieve the documents from the “PlayersList” collection. Then we can display the data associated with those documents from within the interface.

To achieve this, change the helper function from this:

Template.leaderboard.helpers({
    'player': function(){
        return "Some other text";
    }
});

…to this:

Template.leaderboard.helpers({
    'player': function(){
        return PlayersList.find();
    }
});

Here, we’re using the find function that we covered in the previous chapter. This function will retrieve all of the data from the “PlayersList” collection, and because we’ve placed it inside the helper function, this data is now accessible from inside the “leaderboard” template.

But if switch to the browser, we’ll see that the data being output by the “player” tag that’s inside the HTML file doesn’t look quite right.

This is because, previously, the helper function was retrieving a single piece of data – a string that contained the words “Some other text”. But now we’re retrieving an array of the documents from inside the “PlayersList” collection. This means we need to loop through the data that’s returned.

To achieve this, we need to reference the “player” function using a different Spacebars tag.

To begin, remove the following tag from the HTML file:

{{player}}

…and replace it with the following syntax:

{{#each player}}
    test
{{/each}}

Here, we’re creating an each block with the Spacebars syntax, and there’s a few things going on:

First, all of the documents from the “PlayersList” collection are retrieved based on the reference to the “player” function. (These documents are placed inside an array without needing to use the fetch function.)

Second, we loop through the returned data with the each syntax.

Third, we output the word “test” for each document (player) that’s retrieved from the collection. Because there are six players in the collection, the word “test” appears six times.

You can think of this process as like creating an array of players:

var playersList = ['David', 'Bob', 'Mary', 'Bill', 'Warren', 'Tim'];

…and using forEach to loop through the values inside this array:

var playersList = ['David', 'Bob', 'Mary', 'Bill', 'Warren', 'Tim'];
playersList.forEach(function(){
    console.log('test');
});

Within the each block, we can also retrieve the value of the fields from inside the documents. So because we’re retrieving data from the “PlayersList” collection, we can show the values of the “name” and “score” fields that are associated with each player.

To display the player’s names, for instance, refer to the “name” field from inside the each block, wrapping the field name in a double-curly braces:

{{#each player}}
    {{name}}
{{/each}}

Then, to display the player’s scores, do the same for the “score” field:

{{#each player}}
    {{name}}: {{score}}
{{/each}}

But this information will be easier to read as a list, so use the ul and li tags to achieve this:

<ul>
    {{#each player}}
        <li>{{name}}: {{score}}</li>
    {{/each}}
</ul>

After saving the file, the player’s names and scores will now appear inside an unordered list. By default, the players will be sorted by the time they were inserted into the collection – from the players added first to the players added recently – but this is something we’ll change later on.

Summary

In this chapter, we’ve learned that:

  • Meteor handles certain details for us, like the use of html tags and the inclusion of JavaScript and CSS files.
  • By creating templates, we can form a bridge between the application logic and the interface.
  • A project’s code can run on the client and the server, but sometimes, we only want the code in one of these environments. To control where the code should run, we can use the isClient and isServer conditionals.
  • After creating a template, we need to manually include it within the interface. This allows us to control where and when it appears.
  • By creating helper functions, we can execute code from within a template.
  • If a helper function returns an array of data, we can loop through that data from within a template by using the each syntax.

To gain a deeper understanding of Meteor:

  • Realize that templates can be placed anywhere within a project’s folder. We could, for instance, place the “leaderboard” template in another HTML file, and the reference to {{> leaderboard}} would continue to work.
  • Break the application on purpose by moving the each block outside of the “leaderboard” template. If you’re familiar with these sorts of errors, you’ll know how to deal with them when you inevitably make a mistake.
  • Create a helper function that uses find and count to return the number of players in the “PlayersList” collection, and display this data from within the interface.

To browse the project’s code in its current state, click here.