Chapter 7: Sessions

Sessions

When a user clicks on one of the players, a function executes, and when this function executes, we want to change the background color of the li element that surrounds the player that has just been clicked.

This will create the effect of that player being selected.

To achieve this, we’re going to use a feature of Meteor known as sessions, and sessions allow us to store small pieces of data that:

  1. Are not saved to the database.
  2. Will not be remembered on return visits.

This sort of data might not sound immediately useful, but as we’ll see, it’s a surprisingly versatile way of solving a lot of common problems when building a web application (and, specifically, when building an interface).

To begin using sessions, we need to first install a package, and in Meteor, a package is simply a plugin that can:

  1. Add a range of useful features to a project in a matter of seconds.
  2. Reduce the amount of code we need to write and maintain.

By default, every Meteor project has local access to a number of official packages. These packages, designed by the Meteor Development Group, contain functionality that will be used by most developers at some point, but not necessarily for every project, which is why the functionality has been made separate from the core framework and transformed into an optional extra.

(There’s also thousands of third-party packages, made by members of the Meteor community, but these are beyond the scope of this book. You can find them, however, at atmospherejs.com)

Prior to Meteor 1.3, the functionality for creating sessions was included with every project, but it has since been removed from the core framework and turned into a package. Installing packages, fortunately, is quite simple.

Inside the command line, write the following:

meteor add session

Then tap the “Return” key to install the package.

Create a Session

Sessions are an abstract concept, so the best way to understand them is to create one, allowing us to learn by example.

To create a session, add the following code inside the “click .player” event:

Session.set('selectedPlayer', 'session value test');

Here, we’re using this Session.set function and passing through two values:

First, we’re passing through a name for the session. This name is simply a reference for the session. It’s what allows us to refer to the session at a later time. In this case, we’re defining the name as “selectedPlayer”, but you’re free to use whatever name you like.

Second, we’re passing through a value for the session. This is the data we’re storing inside the session. In this case, we’re passing through a string of “session value test”, but we’ll pass through a more interesting value in a moment.

Based on this statement, this session will be created whenever the user clicks on one of the players in the list.

To retrieve the value of this session, we can use a Session.get function:

Session.get('selectedPlayer');

The events block should then resemble:

Template.leaderboard.events({
    'click .player': function(){
        Session.set('selectedPlayer', 'session value test');
        Session.get('selectedPlayer');
    }
});

Here, we’re using this Session.get function and passing through the name of the “selectedPlayer” session that we created a moment ago.

To output the value of this session to the Console, place it inside a variable:

var selectedPlayer = Session.get('selectedPlayer');

Then add a console.log statement beneath this line:

var selectedPlayer = Session.get('selectedPlayer');
console.log(selectedPlayer);

Now, when a user clicks on one of the players in the list, the “session value test” string will be stored inside a session and then immediately output to the Console. This is not particularly useful code – we could achieve this same functionality without using sessions – but now that we’re familiar with the syntax of sessions, we can do something that’s a bit more interesting.

The Player’s ID

When a user clicks on one of the players in the list, we want to grab the unique ID of that player and store it inside the “selectedPlayer” session. This will allow us to then change the background color of that player’s li element.

But if you’re not sure what I mean when I say “the unique ID of the player”, think back to when we inserted players into the “PlayersList” collection. Each time we used the insert function, a random jumble of numbers and letters would appear. That jumble was the unique ID of that particular player.

To implement this feature, create a “playerId” variable at the top of the “click .player” event, and make this variable equal to the same “session value test” string from before:

var playerId = "session value test";

Then modify the Session.set function by passing through the “playerId” variable as the second argument. The event should resemble the following:

'click .player': function(){
    var playerId = "session value test";
    Session.set('selectedPlayer', playerId);
    var selectedPlayer = Session.get('selectedPlayer');
    console.log(selectedPlayer);
}

At this point, the trick is to make the “playerId” variable equal to the unique ID of the player that has just been clicked. This doesn’t require a lot of code, but it does require some explanation.

For the moment, change the “playerId” variable to the following:

var playerId = this._id;

And here, there’s two things going on:

First, we have a reference to this, and as is always the case in JavaScript, the value of this depends on the context. In this particular case, this refers to the Mongo document of the player that has just been clicked.

Second, the _id part is the name of the field inside that player’s Mongo document that holds the unique ID of that player. So in the same way we created a name and a score field, Mongo creates an _id field for each document, and that’s what we’re referencing here. (If you’re curious, the underscore doesn’t have any special significance. It’s simply a part of the field’s name.)

Because of this change, the following is now possible:

  1. A user clicks on one of the players.
  2. That player’s unique ID is stored inside the “playerId” variable.
  3. The value of the “playerId” variable is stored inside the “selectedPlayer” session. (There can only be one value stored inside a single session, so each time a new value is stored, the previous value is overwritten.)
  4. The value of the “selectedPlayer” session is output to the Console.

To see this in action: save the file, switch back to Chrome, and click on any of the players in the list. Their unique ID will appear in the Console.

Since we don’t need to see the unique ID of the clicked player though – we only need access to their ID – we can simply the event to the following:

'click .player': function(){
    var playerId = this._id;
    Session.set('selectedPlayer', playerId);
}

In this code, we’re simply setting the value of the “selectedPlayer” session to the unique ID of the player that the user has just clicked.

Selected Effect, Part 1

When a user clicks on one of the player’s list items, we want to change the background-color property of the li element that contains that player. This will create the effect of that player being selected.

To achieve this, open the project’s CSS file and create a class named “selected”. Attach a background-color property to this class, and pass through a value of “yellow”:

.selected{
    background-color: yellow;
}

Then switch back to the JavaScript file and create a helper named “selectedClass”:

Template.leaderboard.helpers({
    'player': function(){
        return PlayersList.find();
    },
    'selectedClass': function(){
        // code goes here
    }
});

(You’ll notice that both of these helpers are inside the same block of code. As we talked about before, this is possible with the use of commas.)

Within the function itself, return a string of “selected”:

'selectedClass': function(){
    return "selected";
}  

It’s important that the text we’re returning inside this function is equal to the name of the class we defined in the CSS file. So because we created a class of “selected” in the CSS file, we’re returning this text of “selected” from within the function.

Next, switch to the HTML file and place a reference to the “selectedClass” function from inside the li element’s class attribute:

<li class="player {{selectedClass}}">{{name}}: {{score}}</li>

Because of this change, the “selected” class will be applied to all of the player’s list items, thereby changing their background colors to yellow.

This isn’t exactly what we want, but it’s a step in the right direction.

Selected Effect, Part 2

Before we continue, I want to demonstrate something.

Inside the “selectedClass” helper, comment out the return statement:

'selectedClass': function(){
    // return "selected";
}

And, instead, return the value of this._id:

'selectedClass': function(){
    // return "selected"
    return this._id;
}

Here, we’re using this._id to retrieve the unique ID of the player. But instead of the ID being output to the Console, it’ll appear inside the class attribute for each of the li elements. This is not exactly what we want but it’s important to know that, because the “selectedClass” function is being executed from inside the each block, it has access to all of the data associated with each document, including the name field, the score field, and the _id field.

To see this in action: save the file, switch back to Chrome, right click on any of the players, and select the “Inspect Element” option. You’ll notice that the unique ID of each player now appears inside the class attribute.

Knowing this, we’ll do a few things:

First, remove the return statement we just wrote, since this statement was only for demonstration purposes.

Second, uncomment the return statement from earlier since we do want the “selectedClass” function to return the “selected” text.

Third, create a “playerId” variable at the top of the “selectedClass” helper that holds the value of this._id:

'selectedClass': function(){
    var playerId = this._id;
    return "selected"
}

Fourth, create a “selectedPlayer” variable that retrieves the unique ID of the player that’s held within the “selectedPlayer” session:

'selectedClass': function(){
    var playerId = this._id;
    var selectedPlayer = Session.get('selectedPlayer');
    return "selected"
}

Fifth, wrap the return statement in a conditional that checks to see if the “playerId” and “selectedPlayer” variables contain equal values:

'selectedClass': function(){
    var playerId = this._id;
    var selectedPlayer = Session.get('selectedPlayer');
    if(playerId == selectedPlayer){
        return "selected"
    }
}

If you’re having trouble following the logic though, here’s what’s going on:

When a user clicks on one of the player’s list items, the unique ID of that player is stored inside the “selectedPlayer” session.The ID in that session is then matched against all of the IDs of the players in the list.

Because the player’s ID will always be unique – this is something that Mongo takes care of – there can only be one match. When that match occurs, the text of “selected” is returned by the “selectedClass” function and placed inside the class attribute for that particular player’s list item.

As a result of the “selected” class being applied to a player’s list item, the background color of that list item will change to yellow.

This creates the effect a player being selected whenever they’re clicked.

If you’re a little confused though, fear not. This is a common solution to a common problem when building web applications – there’s a lot of times where it’s important to highlight what the user has selected, after all – but it’s also the most convoluted example in this book.

As long as you can understand that the unique ID of a player is stored inside a session when that player is clicked, you’re ready to continue reading.

Summary

In this chapter, we’ve learned that:

  • Packages allow us to quickly add functionality to an application. There’s a handful of official packages, and thousands of third-party packages.
  • Sessions are used to store small pieces of data that isn’t saved to the database or remembered on return visits.
  • To create a session we use the Session.set function.
  • To retrieve the value of a session, we use the Session.get function.
  • Helper functions and events that are inside an each block have access to the data that’s being iterated through by that block.

To gain a deeper understanding of Meteor:

  • Consider how else we might use the value of the “selectedPlayer” session. What else could we achieve with the unique ID of the selected player?

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