Lesson Monday

After following along with the weekend homework, we have a simple address book application that can create contacts and add, delete, and find them in an address book. Now let's start building the UI!

In the process, we'll experiment with adding dynamic elements to the DOM and learn about new concepts like event bubbling and event delegation. These are more advanced jQuery concepts that you won't be expected to apply to this Friday's independent project. However, you are encouraged to experiment with them, as they'll make you a better coder in the long run.

HTML

Let's get the basics of our UI up and running. We'll create an index.html file in the top-level of our directory with the following HTML boilerplate:

index.html
<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>

  </body>
</html>

Next let's fill in each section. First we'll add links to our <head> section:

index.html
<!DOCTYPE html>
<html>
  <head>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="js/scripts.js"></script>
    <title>Address Book</title>
  </head>
  <body>

  </body>
</html>

We've added the following, which should all be review:

  • A link to the Bootstrap CDN.
  • A link to a custom local CSS stylesheet. (This stylesheet doesn't exist yet, but we'll create it in a moment.)
  • A link to the jQuery CDN.
  • A link to our local scripts.js file containing our JavaScript Logic.
  • A <title> for our page.

Now let's focus on the <body>. We'll keep it simple before later adding Bootstrap classes for styling and organization:

index.html
...

<body>
  <h1>Address Book</h1>
  <h2>Add a Contact:</h2>
  <form id="new-contact">
    <label for="new-first-name">First Name</label>
    <input type="text" id="new-first-name">
    <label for="new-last-name">Last Name</label>
    <input type="text" id="new-last-name">
    <label for="new-phone-number">Phone Number</label>
    <input type="text" id="new-phone-number">
    <button type="submit">Add</button>
  </form>

  <h2>Contacts:</h2>
  <ul id="contacts">
  </ul>

  <div id="show-contact">
    <p>First Name: <span class="first-name"></span></p>
    <p>Last Name: <span class="last-name"></span></p>
    <p>Phone Number: <span class="phone-number"></span></p>
    <div id="buttons">
    </div>
  </div>
</body>

...

Here we have:

  • A form to add new contacts, including fields for first names, last names, and phone numbers. Each field also has a corresponding <label>.

  • A <ul> element with an id of contacts, where our list of Contacts will be populated with jQuery.

  • A show-contact div that will eventually display the details for a specific contact when selected from the list.

CSS

Next let's add CSS. We'll create a css subdirectory with a styles.css file inside containing a single rule:

css/styles.css
#show-contact {
  display: none;
}

This ensures the show-contact div is hidden until we later activate jQuery to display it. (This area will later display details of a Contact when one is selected from the list.)

JavaScript

After following along with the past few lessons we should already have a scripts.js file in a js directory that looks like this:

scripts.js
// Business Logic for AddressBook ---------
function AddressBook() {
  this.contacts = [],
  this.currentId = 0
}

AddressBook.prototype.addContact = function(contact) {
  contact.id = this.assignId();
  this.contacts.push(contact);
}

AddressBook.prototype.assignId = function() {
  this.currentId += 1;
  return this.currentId;
}

AddressBook.prototype.findContact = function(id) {
  for (var i=0; i< this.contacts.length; i++) {
    if (this.contacts[i]) {
      if (this.contacts[i].id == id) {
        return this.contacts[i];
      }
    }
  };
  return false;
}

AddressBook.prototype.deleteContact = function(id) {
  for (var i=0; i< this.contacts.length; i++) {
    if (this.contacts[i]) {
      if (this.contacts[i].id == id) {
        delete this.contacts[i];
        return true;
      }
    }
  };
  return false;
}

// Business Logic for Contacts ---------
function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName,
  this.lastName = lastName,
  this.phoneNumber = phoneNumber
}

Contact.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
}

Below this business logic we'll add a new section for user interface logic, denoted by another comment:

scripts.js
...

// User Interface Logic ---------
var addressBook = new AddressBook();

$(document).ready(function() {
  $("form#new-contact").submit(function(event) {
    event.preventDefault();
    var inputtedFirstName = $("input#new-first-name").val();
    var inputtedLastName = $("input#new-last-name").val();
    var inputtedPhoneNumber = $("input#new-phone-number").val();
    var newContact = new Contact(inputtedFirstName, inputtedLastName, inputtedPhoneNumber);
    addressBook.addContact(newContact);
    console.log(addressBook.contacts);
  })
})

Let's walk through this new code:

  • We create a new AddressBook object named addressBook. This is a global variable because it's declared at the 'top level' of our file. That is, it's not inside of a function or method, and is therefore available to the entire file (hence the name global variable). While we generally want to avoid working with global variables, we make an exception because we're using this global variable to mimic a database.

  • We then open a $(document).ready(function()... as we've done in previous lessons.

  • We add a form submission event listener, with the standard event.preventDefault() included.

  • We gather user-provided form input from form fields for first name, last name, and phone number, and assign them to variables (inputtedFirstName, inputtedLastName, etc.)

  • We create a new Contact object, passing in this gathered information as arguments.

  • We add the newContact to our AddressBook using the addContact() method.

  • Finally, we log the list of Contacts in our AddressBook to the console, to double-check the new contact has been added. (We'll add logic for displaying contacts in our user interface in the next lesson.)

The entire updated scripts.js file looks like this:

scripts.js
// Business Logic for AddressBook ---------
function AddressBook() {
  this.contacts = [],
  this.currentId = 0
}

AddressBook.prototype.addContact = function(contact) {
  contact.id = this.assignId();
  this.contacts.push(contact);
}

AddressBook.prototype.assignId = function() {
  this.currentId += 1;
  return this.currentId;
}

AddressBook.prototype.findContact = function(id) {
  for (var i=0; i< this.contacts.length; i++) {
    if (this.contacts[i]) {
      if (this.contacts[i].id == id) {
        return this.contacts[i];
      }
    }
  };
  return false;
}

AddressBook.prototype.deleteContact = function(id) {
  for (var i=0; i< this.contacts.length; i++) {
    if (this.contacts[i]) {
      if (this.contacts[i].id == id) {
        delete this.contacts[i];
        return true;
      }
    }
  };
  return false;
}

// Business Logic for Contacts ---------
function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName,
  this.lastName = lastName,
  this.phoneNumber = phoneNumber
}

Contact.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
}

// User Interface Logic ---------
var addressBook = new AddressBook();

$(document).ready(function() {
  $("form#new-contact").submit(function(event) {
    event.preventDefault();
    var inputtedFirstName = $("input#new-first-name").val();
    var inputtedLastName = $("input#new-last-name").val();
    var inputtedPhoneNumber = $("input#new-phone-number").val();
    var newContact = new Contact(inputtedFirstName, inputtedLastName, inputtedPhoneNumber);
    addressBook.addContact(newContact);
    console.log(addressBook.contacts);
  })
})

Great! We can now launch our index.html page in the browser, fill out our form several times, and see a growing list of Contacts logged in the console! We're now ready for the next step: displaying contacts in our application and adding dynamic IDs.


Example GitHub Repo for the Address Book

Examples


HTML

index.html
<!DOCTYPE html>
<html>
  <head>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="js/scripts.js"></script>
    <title>Address Book</title>
  </head>
  <body>
    <h1>Address Book</h1>
    <h2>Add a Contact:</h2>
    <form id="new-contact">
      <label for="new-first-name">First Name</label>
      <input type="text" id="new-first-name">
      <label for="new-last-name">Last Name</label>
      <input type="text" id="new-last-name">
      <label for="new-phone-number">Phone Number</label>
      <input type="text" id="new-phone-number">
      <button type="submit">Add</button>
    </form>

    <h2>Contacts:</h2>
    <ul id="contacts">
    </ul>

    <div id="show-contact">
      <p>First Name: <span class="first-name"></span></p>
      <p>Last Name: <span class="last-name"></span></p>
      <p>Phone Number: <span class="phone-number"></span></p>
      <div id="buttons">
      </div>
    </div>
  </body>
</html>

CSS

styles.css
#show-contact {
  display: none;
}

JS

scripts.js
// Business Logic for AddressBook ---------
function AddressBook() {
  this.contacts = [],
  this.currentId = 0
}

AddressBook.prototype.addContact = function(contact) {
  contact.id = this.assignId();
  this.contacts.push(contact);
}

AddressBook.prototype.assignId = function() {
  this.currentId += 1;
  return this.currentId;
}

AddressBook.prototype.findContact = function(id) {
  for (var i=0; i< this.contacts.length; i++) {
    if (this.contacts[i]) {
      if (this.contacts[i].id == id) {
        return this.contacts[i];
      }
    }
  };
  return false;
}

AddressBook.prototype.deleteContact = function(id) {
  for (var i=0; i< this.contacts.length; i++) {
    if (this.contacts[i]) {
      if (this.contacts[i].id == id) {
        delete this.contacts[i];
        return true;
      }
    }
  };
  return false;
}

// Business Logic for Contacts ---------
function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName,
  this.lastName = lastName,
  this.phoneNumber = phoneNumber
}

Contact.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
}

// User Interface Logic ---------
var addressBook = new AddressBook();

$(document).ready(function() {
  $("form#new-contact").submit(function(event) {
    event.preventDefault();
    var inputtedFirstName = $("input#new-first-name").val();
    var inputtedLastName = $("input#new-last-name").val();
    var inputtedPhoneNumber = $("input#new-phone-number").val();
    var newContact = new Contact(inputtedFirstName, inputtedLastName, inputtedPhoneNumber);
    addressBook.addContact(newContact);
    console.log(addressBook.contacts);
  })
})

Example GitHub Repo for the Address Book