Dog pack

The story so far

Persisting record sets

There's a problem with the course data app in the previous lesson. It doesn't remember anything. We created a record set when the page loads:

(function($) {
    //Initialize course catalog.
    var catalog = [
        {
            id: 42,
            title: "Web apps for happy dogs",
            maxEnrollment: 40,
            location: "Monty Hall 301"
        },
        ...
    ];

It was lost when the page ended.

We want something like this:

(function($) {
    //Initialize course catalog.
    var catalog = LOAD RECORD SET FROM LOCALSTORAGE

We're going to use a JS library called TaffyDB to do exactly that. The <script> tag to load Taffy is not part of our standard template. You need to add this line:

<script src="https://cdnjs.cloudflare.com/ajax/libs/taffydb/2.7.3/taffy-min.js"></script>

Taffy does many things, but let's start with the basics:

  • Putting a record set into localStorage
  • Loading a record set from localStorage

Let's write an app that knows about a dog pack. It will start with two pages:

  • A page that creates a record set, and puts it in localStorage
  • A page that loads the record set from localStorage, and shows it

Creating a pack

Check out the page that creates the record set. It looks like this:

Initialize pack

Click the button, and it will create a record set, and use Taffy to put it into localStorage.

Here's some psuedocode, for what we want to do when the button is clicked:

Create the record set in a JS array
Make a TaffyDB
Link the TaffyDB to localStorage
Make sure the TaffyDB is empty
Insert the record set into the TaffyDB

The first line, creating the record set, you've seen before.

var dogPack = [
    {
        id: 11,
        name: "Bufford",
        description: "She can sniff it out.",
        weight: 26
    },
    ...
];

We can add as many dogs as we like.

That's the first task done:

Create the record set in a JS array  DONE THIS
Make a TaffyDB  NOW DO THIS
Link the TaffyDB to localStorage
Make sure the TaffyDB is empty
Insert the record set into the TaffyDB

Here's how you make a TaffyDB:

//Create an empty Taffy DB.
DogPack.packDb = TAFFY();

Notice that I put packDb into the DogPack namespace. I could have done this:

//Create an empty Taffy DB.
var packDb = TAFFY();

Here, packDb is just a variable.

Putting packDb in the namespace is optional in this program, but will make some of our future work easier.

What's the next step?

Create the record set in a JS array  DONE THIS
Make a TaffyDB  DONE THIS
Link the TaffyDB to localStorage  NOW DO THIS
Make sure the TaffyDB is empty
Insert the record set into the TaffyDB

These lines will make a TaffyDB linked to localStorage:

//Link the DB to localStorage. Existing items will be loaded.
DogPack.packDb.store("dogPack");

The name of the store() function is a bit misleading. It seems like it would, well, store something. But it doesn't.

What the store() function does is bind DogPack.packDb to the localStorage item with the key dogPack (remember that every item in localStorage has a key).

This binding gives us JS magic. Any changes we make to DogPack.packDb automatically change localStorage. So, if we add a record to DogPack.packDb, TaffyDB automatically changes localStorage. If we delete a record in DogPack.packDb, TaffyDB automatically changes localStorage.

Back to the pseudocode:

Create the record set in a JS array  DONE THIS
Make a TaffyDB  DONE THIS
Link the TaffyDB to localStorage  DONE THIS
Make sure the TaffyDB is empty  NOW DO THIS
Insert the record set into the TaffyDB

We don't know what's in localStorage already. Let's erase what's there, to be safe.

//Remove all items.
DogPack.packDb().remove();

Because DogPack.packDb is bound to localStorage, erasing everything from DogPack.packDb also erases everything from localStorage with the key dogPack.

The last thing:

Create the record set in a JS array  DONE THIS
Make a TaffyDB  DONE THIS
Link the TaffyDB to localStorage  DONE THIS
Make sure the TaffyDB is empty  DONE THIS
Insert the record set into the TaffyDB  NOW DO THIS

Remember that we made the record set earlier:

var dogPack = [
    {
        id: 11,
        name: "Bufford",
        description: "She can sniff it out.",
        weight: 26
    },
    ...
];

So…

//Insert new records into the DB.
DogPack.packDb.insert(dogPack);

Here's the entire code for Initialize button:

<button id="initialize" class="btn btn-primary" onclick="DogPack.initPack()">Initialize</button>
...
DogPack.initPack = function() {
    //Set up initial data set.
    var dogPack = [
        {
            id: 11,
            name: "Bufford",
            description: "She can sniff it out.",
            weight: 26
        },
        ...
    ];
    //Create an empty Taffy DB.
    DogPack.packDb = TAFFY();
    //Link the DB to localStorage. Existing items will be loaded.
    DogPack.packDb.store("dogPack");
    //Remove all items.
    DogPack.packDb().remove();
    //Insert new records into the DB.
    DogPack.packDb.insert(dogPack);
    alert("Done");
};

There's an alert() at the end, so you know something happened.

Sometimes there's () (parentheses) after DogPack.packDb, as in the remove() call. Sometimes, there's not, as in the store() call. Rather than trying to figure out why, just copy the code you need from this page, or one of the later ones.

Showing the record set

Try the page that shows the pack. Assuming you have initialized the record set, you'll see:

List

Here's the code:

<div id="packContainer" class="hide-on-load"></div>
...
$(document).ready(function () {
    //Load the navbar from the library.
    $("#navbar").load("library/includes/navbar.html");
    //Load the footer from the library.
    $("#footer").load("library/includes/footer.html");
    //Create an empty Taffy DB.
    DogPack.packDb = TAFFY();
    //Link the DB to localStorage. Existing items will be loaded.
    DogPack.packDb.store("dogPack");
    //Start building the output table.
    var packHtml =
        "<table class='table'>" +
        "  <thead>" +
        "      <th>Name</th>" +
        "      <th>Description</th>" +
        "      <th>Weight (kg)</th>" +
        "  </thead>" +
        "  <tbody>";
    //Sort by name, then generate HTML for each record.
    DogPack.packDb().order("name").each(function (dog) {
        var dogRow =
            "<tr>" +
            "  <td>" + dog.name + "</td>" +
            "  <td>" + dog.description + "</td>" +
            "  <td>" + dog.weight + "</td>" +
            "</tr>";
        packHtml += dogRow;
    });
    //Finish the pack HTML.
    packHtml += "</tbody></table>";
    //Show it.
    $("#packContainer")
        .html(packHtml)
        .show('slow');
});

The first line…

<div id="packContainer" class="hide-on-load"></div>

… creates a place to show the table.

These lines create a TaffyDB and bind it to localStorage:

//Create an empty Taffy DB.
DogPack.packDb = TAFFY();
//Link the DB to localStorage. Existing items will be loaded.
DogPack.packDb.store("dogPack");

It's the same code that was on the initialization page.

Earlier, we made a for forEach() loop to show a record set. The loop created some HTML. This is the code from before:

catalog.forEach(function(course){
    var courseHtml =
        "<ul>" +
        "  <li>Title: " + course.title + "</li>" +
        "  <li>Maximum enrollment: " + course.maxEnrollment + "</li>" +
        "  <li>Location: " + course.location + "</li>" +
        "</ul>";
    catalogHtml += courseHtml;
});

We're going to do the same thing, but change the loop syntax for TaffyDB. Here's how we'll show the pack:

DogPack.packDb().order("name").each(function (dog) {
    var dogRow =
        "<tr>" +
        "  <td>" + dog.name + "</td>" +
        "  <td>" + dog.description + "</td>" +
        "  <td>" + dog.weight + "</td>" +
        "</tr>";
    packHtml += dogRow;
});

DogPack.packDb().order("name") says to sort the record set by the property name. Then, for each record…, well, the same thing that forEach does, except that forEach is called each.

That's it! Inject the final HTML into the output container and show it:

$("#packContainer")
    .html(packHtml)
    .show('slow');

Directories

This app has two pages:

  • Create and save a record set
  • Show a record set

The first page alters the data. The second does not.

If this were a business app, the first page would be available only to people inside the business, admins of some type. You wouldn't want customers to use the page.

The second page would be available to everyone, customers included.

  • Create and save a record set ADMINS ONLY
  • Show a record set PUBLIC

You'll learn how to add a login system later.

There is something you can do now, though. Usually, protected pages are kept in a different directory from public pages. Here's how the files might look:

Files

Files we want only admins to use are in the admin directory. This is a good habit to get into.

Exercise

Exercise: Fish list
Write an app listing fish.

It has two pages.

The first pages initializes the fish data. It looks like this:

Initialize

Clicking the button initializes a TaffyDB database bound to localStorage.

Put the page in an admin directory.

The second page lists the fish, sorted by name. For example:

Fish list

You can change the fish data, if you want. Use whatever fields you like, as long as at least two fields are shown.

Upload the URL of your solution.

(If you were logged in as a student, you could submit an exercise solution, and get some feedback.)

Summary