8.

1 - Databases, Part 2
In this chapter, we're going to finish the remaining features of the original Leaderboard application. Then we'll start working on some new features.

To begin, we're going to create a "Give 5 Points" button that, when
clicked, will increment the value of the selected player's "score" field.

To do this, create a button inside the "leaderboard" template.

This button should be outside of the "each" block and have a class attribute of "increment".

To then make this button do something, create a "click" event that's attached to the "increment" class. This event will now trigger when the "Give
5 Points" button is clicked.

Within the event, we need to grab the unique ID of the selected player.

We can then use that ID to find a player inside the "PlayersList" collection and increment the value of that player's "score" field by 5.

To access the unique ID of the selected player, use the "Session.get" function to retrieve the value of the "selectedPlayer" session, and store the
result in a "selectedPlayer" variable.

Then output this variable to the Console.

Because of this code, users can select any of the players in the list,
click the "Give 5 Points" button, and then see that player's ID in the Console.

1

Mongo Operators
Inside the "click increment" event, remove the console.log statement and
replace it with a "PlayersList.update" function.

This update function allows us to modify a document that's already in the
collection, which will allow us to increment the selected player's score.

There are two pieces of data we need to pass into the update function.

First, we need to pass through the ID of a document, and since we want to
update the document of the currently selected player, we can pass through
the "selectedPlayer" variable.

Second, we need to pass through some data in the JSON format, and whatever
data we pass through here is how the document for the selected player will
be modified.

Based on this change, this statement will find the document of the currently selected player and change that selected player's "score" field to "5".

But there is a problem.

If you select a player and click the "Give 5 Points" button, the name of
that player will disappear. The value of the "score" field will change to
"5", just as planned, but the "name" field will be completely removed from
the document.

You can see proof of this by using the find and fetch function.

This might seem like a bug but, by default, the update function works by
deleting the original document and creating a new document with the data
that we specify.

2

The value of the “_id" field will remain the same, but because we've only
specified the "score" field inside the update function, that's the only
other field that will continue to exist after the document is modified.

To account for this, we need to use a Mongo feature that allows us to set
the value of the "score" field without deleting the original document.

Inside the update function, pass through a "set" operator as the second argument, making sure to precede this operator with a dollar-sign. This operator allows us to modify fields in a document without deleting the original
document.

After the colon, we just have to pass through the fields we want to modify,
along with their values.

Because of this change, the update function won't be completely broken.

If we select a player and click the "Give 5 Points" button, the value of
the "score" field of that player's document will change without affecting
the rest of the document.

But we still haven't built the feature we wanted to build.

At the moment, we're only setting the value of the "score" field, rather
than incrementing it. So no matter how many times we click the button, the
value of the "score" field will never go any higher than "5".

To fix this problem, replace the "set" operator with an "inc" operator.

By using this special Mongo operator, whenever the update function is triggered, the value of the selected player's "score" field will now be incremented by a value of "5".

What's also neat is how easily we can allow users to decrement the scores
of players using very similar code.

3

Inside the "leaderboard" template, create a "Take 5 Points" button with a
class of "decrement".

Then, inside the JavaScript file, create a copy of the "click increment"
event, making sure to separate the events with commas.

At this point, we only need to make two changes:

First, change the selector from "increment" to "decrement". This ensures
that the event is attached to the correct button.

And second, pass a value of minus 5 into the "inc" operator, rather than a
value of "5". This reverses the effect of the operator, meaning the "Take 5
Points" button will now decrement the selected player's score.

Sorting Documents
By default, the players in our list are ranked by the time they were added
to the "PlayersList" collection, rather than being ranked by their scores.

To fix this, we'll modify the "player" helper function.

To begin, pass through a pair of curly braces into the find function.

By using these curly braces, we're explicitly stating that we want to retrieve all of the data from the "PlayersList" collection. This is the default behavior of the find function, but by passing through the curly
braces as the first argument, we can pass through a second argument, and
that's what we need to do.

As the second argument for the find function, pass through another pair of
curly braces, along with a "sort" method.

4

This method allows us to define how we want to sort the retrieved documents.

Pass through the name of whatever fields you want to sort by, which in this
case is the "score" field, and also pass through a value of "-1".

By passing through a value of "-1", we can sort the documents in descending
order. This means players will be sorted from the highest score to the lowest score. If we passed through a value of "1", players would be sorted
from the lowest score to the highest score.

Based on this change, players will be now ranked by their scores.

But what happens if two players have the same score?

Consider the "Bob" and "Bill" players, for instance.

If these players have the same score, Bill should be ranked above Bob because, alphabetically, his name comes first.

But at the moment, this won't happen because Bob was added to the collection before Bill.

To fix this, pass the "name" field into the "sort" method, but this time,
pass through a value of "1" instead of "-1".

The players will still be primarily sorted by their scores, but if any
players have the same scores, those players will be additionally sorted by
their names.

Individual Documents
When a user selects one of the players in the list, that player's name
should appear beneath the list.

5

To achieve this, create a "selectedPlayer" helper function for the "leaderboard" template.

Within this function, retrieve the ID of the currently selected player using the "Session.get" function and store this inside a "selectedPlayer"
variable.

Then create a return statement that uses a findOne function to retrieve the
selected player's document.

We haven't talked about the "findOne" function before, but this function
has a couple of advantages over the regular find function:

First, we can pass through the ID of a document as the only argument.

And second, there's a performance benefit, because while the find function
will search through the collection for all possible matches to a query, the
findOne function will stop searching as soon as a match is found.

As such, if you ever need to only retrieve a single document, it's best to
use the findOne function.

With this function in place, place a reference to the "selectedPlayer"
function inside the "leaderboard" template. I'm placing mine at the bottom
of the player's list, between a pair of list item tags.

But if we save the file, the output won't look quite right, and that's because the "findOne" function is retrieving the player's entire document.

To fix this, we need to specify that we only want to retrieve the value of
the document's "name" field. This can be achieved with dot notation.

We should also make it so the template doesn't attempt to show a player's
name if a player isn't selected.

6

This can be achieved with a conditional in the Spacebars syntax.

We can also use this same conditional to make sure the interface buttons
don't appear unless a player is currently selected.

7