Pizza

The pizza site

Our next project is a pizza site. It builds on the Dreamteam project but adds some new features:

The final version with all code examples can be found here:

Using a new css grid stylesheet

For the pizza site I am using a css grid layout from MDN. It is very well documented and easy to adjust to student projects.

Optimised pizza array creation

The first version of the pizza program works the same way as the Dreamteam project:

function Pizza(pizzaName, pizzaType, pizzaPrize, garlic) {
  this.pizzaName = pizzaName;
  this.pizzaType = pizzaType;
  this.pizzaPrize = pizzaPrize;
  this.garlic = garlic;
}

const pizza1 = new Pizza('Margherita', "normal", 50, false);
const pizza2 = new Pizza('Hawaii', "normal", 70, false);
const pizza3 = new Pizza('Gorgonzola', "normal", 75, true);
const pizza4 = new Pizza('Calzone', "calzone", 70, false);
const pizza5 = new Pizza('Pantalone', "calzone", 80, true);

let pizzas = [];
pizzas.push(pizza1);
pizzas.push(pizza2);
pizzas.push(pizza3);
pizzas.push(pizza4);
pizzas.push(pizza5);

The Pizza function creates pizzas. We call it 5 times with the values we want for the pizza objects. And finally we push the pizzas into the pizzas array

A few problems come to mind here:

  1. Is it really necessary to have one line of javascript code for each pizza object we want to push into the array?
  2. What if we forget to push one of the pizza objects into the array?

So let us see if we can tackle these objections in order.

Let us first do a simple refactoring of the push:

function Pizza(pizzaName, pizzaType, pizzaPrize, garlic) {
  this.pizzaName = pizzaName;
  this.pizzaType = pizzaType;
  this.pizzaPrize = pizzaPrize;
  this.garlic = garlic;
}

const pizza1 = new Pizza('Margherita', "normal", 50, false);
const pizza2 = new Pizza('Hawaii', "normal", 70, false);
const pizza3 = new Pizza('Gorgonzola', "normal", 75, true);
const pizza4 = new Pizza('Calzone', "calzone", 70, false);
const pizza5 = new Pizza('Pantalone', "calzone", 80, true);

let pizzas = [];
pizzas.push(pizza1, pizza2, pizza3, pizza4, pizza5);

By calling the push function only once we can hopefully make fewer mistakes. But what about the second problem? Can we somehow make the process more robust?

let pizzas = [];

function Pizza(pizzaName, pizzaType, pizzaPrize, garlic) {
  this.pizzaName = pizzaName;
  this.pizzaType = pizzaType;
  this.pizzaPrize = pizzaPrize;
  this.garlic = garlic;
}

function addPizza(pizzaName, pizzaType, pizzaPrize, garlic) {
  let p = new Pizza (pizzaName, pizzaType, pizzaPrize, garlic);

  pizzas.push(p);
}

addPizza('Margherita', "normal", 50, false);
addPizza('Hawaii', "normal", 70, false);
addPizza('Gorgonzola', "normal", 75, true);
addPizza('Calzone', "calzone", 70, false);
addPizza('Pantalone', "calzone", 80, true);

Let us review what we did here: We added a new addPizza function that takes the pizza values as arguments, creates a variable p, calls the Pizza function and pushes the pizza into the array. As the pizza object is created and pushed into the array at the same time, this eliminates the second problem. Note that we have to declare the pizzas array first to ensure that the addPizza function has access to it.

Using let and const instead of var

You might have noticed a slight change in the examples above. Instead of using var to declare variables we use let and const.

Using let and const prevents different types of errors in the code.

  1. The let expression prevents the variable from being used outside the block scope, thereby preventing clashes between variables with the same name (which should be avoided anyway).

  2. The const expression assures that the value of the variable can never change. This prevents the programmer from accidentally assigning two different values to a variable.

A good explanation for using them can be found in this article. There is also more information in this longer article about the new features in ES2015

Using the for-of loop instead of the old for loop

In the Dream Team project we used the for loop for all iterations over objects in the array. But as you might have noticed from the code link we use a new form of loop in the pizza - the for...of loop. The main advantage over the old for loop is that we do not need an iterator. Compare these two different ways of displaying all pizzas:

Traditional for loop:

function displayAllPizzas(pizzas) {
let pizzaString = "";
let pizzaArraySize = pizzas.length;
for (i = 0; i < pizzaArraySize; i++) {
pizzaString += pizza.pizzaName + ", "
+ pizza.pizzaType + ", "
+ pizza.pizzaPrize + ", "
+ pizza.garlic + "<br/>";
}
return pizzaString;
}

Using the for...of loop:

function displayAllPizzas(pizzas) {
let pizzaString = "";
for (pizza of pizzas) {
pizzaString += pizza.pizzaName + ", "
+ pizza.pizzaType + ", "
+ pizza.pizzaPrize + ", "
+ pizza.garlic + "<br/>";
}
return pizzaString;
}

In the first example we need to find the length of the array, then iterate over the pizza object by starting with the first object (0 since javascript counts arrays from zero). In the second example we simply tell the program that we will iterate over all objects in the array. This is much cleaner and reduces the risk of errors in the code. Note that the pizza variable could be named anything as it is a local variable used only in the displayAllPizzas function.

A very nice guide with an overview of loop structures

Using innerHTML to show the result of functions

Probably the biggest change from the Dream Team project is that the output from the functions are now a part of the site. So instead of using alert to show the result we now show it as part of the HTML code. This requires four changes to the codebase:

  1. The displayAllPizzas function now returns a string with HTML code that goes inside the code in the index.html file. Since we are now returning HTML and not text (as we did when using the alert function), we now have to return the text followed by a <br/> tag in order to have line breaks between the pizzas. Note also the use of the for...of loop (see above).

    function displayAllPizzas(pizzas) {
    let pizzaString = "";
    for (pizza of pizzas) {
     pizzaString += pizza.pizzaName + ", "
     + pizza.pizzaType + ", "
     + pizza.pizzaPrize + ", "
     + pizza.garlic + "<br/>";
    }
    return pizzaString;
    }
    
  2. In the script.js file you need the following code

     document.getElementById("listAll").innerHTML = displayAllPizzas(pizzas);
    

    This tells javascript to look for a HTML tag with an id of listAll and to place the output of the displayAllPizzas function inside this HTML tag.

  3. In the index.html file you need the following code

             <p id="listAll"></p>
    

    This <p> tag has an id of listAll and serves as a placeholder for the output of the displayAllPizzas function.

  4. Finally, we have to make sure that the script.js file is imported in the bottom of the index.html file. In the Dream Team project this did not matter as we did not use the innerHTML function. But now the index.html needs to be parsed (read by the browser) before calling the javascript function. Otherwise we will get an error message saying that it does not know the listAll id.

Modifying the display function

Turning the pizza list into a numbered list

If we want to display the pizza list as a numbered it only requires a few modifications:

First we modify the display function so that it outputs list items. We do that by adding a <li> tag to each of the pizzas as they are diplayed. Note that we need an opening and closing <li> tag as we are not just creating line breaks.

function displayPizzaNumbered(pizzas) {
  let pizzaString = "";
  for (pizza of pizzas) {
    pizzaString += "<li>" + pizza.pizzaName
    + ", " + pizza.pizzaType
    + ", " + pizza.pizzaPrize
    + ", " + pizza.garlic + "</li>";
  }
  return pizzaString;
}

To show the list we will make the following addition to the script.js file:

document.getElementById("listNumbered").innerHTML = displayPizzaNumbered(pizzas);

And make the following addition to the index.html file:

<ol id="listNumbered"></ol>

The <ol> tag means “ordered list” so when we put list item (<li>) inside these tags we create an ordered list. If we wanted a bulleted list we would only need to replace the <ol> tag with <ul> (unordered list).

Creating conditions and breaks

Another advantage of using the for...of loop is that it is easy to create breaks in the loop. If we want to display only pizzas with garlic we can modify the loop as follows:

function displayGarlicPizzas(pizzas) {
  let pizzaString = "";
  for (pizza of pizzas) {
    if (pizza.garlic == false) {
      continue;
    }
    pizzaString += pizza.pizzaName + ", "
    + pizza.pizzaType + ", "
    + pizza.pizzaPrize + ", "
    + pizza.garlic + "<br/>";
  }
  return pizzaString;
}

This filters out the pizzas without garlic from the list.

To show the list we will make the following addition to the script.js file:

document.getElementById("listGarlic").innerHTML = displayGarlicPizzas(pizzas);

And make the following addition to the index.html file:

<p id="listGarlic"></p>

Using equality checks

The use of equality checks in javascript can be confusing. The very short recommendation is to use the === operator unless you are sure that you want to do a loose comparison.

More info can be found in this article