Lesson Sunday

Now that we know how to loop through an object's keys (properties), let's update our user interface code so we can display all of the Contacts on our site.

Displaying Dynamic Contact Data

We'll start by writing an empty displayContactDetails() method below our global variable but above our form submission code:

scripts.js
...

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

function displayContactDetails(addressBookToDisplay) {
}

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

As the function name states, this method will display the details of contacts. It will takes an AddressBook object as an argument. Also, notice we're not putting it directly into the block of code with our form submission listener. This is a best practice. By separating our code out, it's cleaner, easier to read, and easier to debug and refactor.

Now let's actually write the code that goes in this method. We'll apply new best practices along the way too:

scripts.js
...

function displayContactDetails(addressBookToDisplay) {
  let contactsList = $("ul#contacts");
  let htmlForContactInfo = "";
  Object.keys(addressBookToDisplay.contacts).forEach(function(key) {
    const contact = addressBookToDisplay.findContact(key);
    htmlForContactInfo += "<li id=" + contact.id + ">" + contact.firstName + " " + contact.lastName + "</li>";
  });
  contactsList.html(htmlForContactInfo);
}

...
  • First we save our jQuery ul#contacts element in a variable called contactsList. This is a best practice because it takes jQuery time to query the DOM and find ul#contacts. Saving it in a variable prevents our application from needing to check the DOM again if we need to use the selector multiple times. This is much more efficient. In general, we want to query the DOM as little as possible. Querying the DOM once and saving values in a variable is always going to be better than querying the DOM multiple times.

  • Next, we create a string called htmlForContactInfo. The purpose of this variable is to construct all the HTML for our contact list before querying the DOM. We'll discuss this further in a moment.

  • Then we use Object.keys() to get all the keys from addressBookToDisplay.contacts so we can iterate through them.

  • In our loop, we grab each contact by using our AddressBook.prototype.find() method.

  • Finally, we construct our HTML as a string and append it to htmlForContactInfo. You might wonder why we are going to the trouble to construct this string instead of just appending each contact to the DOM one at a time. However, as we just discussed, querying the DOM takes time. It is actually quite a slow process as far as coding is involved - much slower than most other code we'll write. Let's say, for instance, that our address book has one hundred contacts. By constructing our HTML string all at once, we only have to query the DOM once instead of one hundred times. This is much, much more efficient.

  • Let's take a look at what's actually getting appended to the HTML string with each contact:

"<li id=" + contact.id + ">" + contact.firstName + " " + contact.lastName + "</li>"

Each contact is being attached to an <li> with a dynamic id matching the Contact's id property. This is very important. We can later retrieve this id stored in the <li> to use with our AddressBook.prototype.findContact() method to locate an entire Contact object. If we didn't attach this information to the HTML string, our application would have no way to easily get the ID.

Let's call this new displayContactDetails function whenever we add a new Contact. All we have to do is remove our console.log() and replace it with this new function.

scripts.js
...

$(document).ready(function() {
  $("form#new-contact").submit(function(event) {
    event.preventDefault();
    const inputtedFirstName = $("input#new-first-name").val();
    const inputtedLastName = $("input#new-last-name").val();
    const inputtedPhoneNumber = $("input#new-phone-number").val();
    let newContact = new Contact(inputtedFirstName, inputtedLastName, inputtedPhoneNumber);
    addressBook.addContact(newContact);
    displayContactDetails(addressBook);  // <--- This is the new line!
  });
});

Now each time we add a new Contact, the page will update and add the contact's first and last names to the list of contacts. Try it out for yourself in the browser.

If we open Chrome Developer Tools and inspect elements on the page (right-click and then select Inspect) we'll see each Contact's <li> entry has a unique id corresponding to the Contact object's automatically-assigned id property. The GIF below demonstrates this:

In the image, we can see that both of the contacts created have a dynamic ID.

While this dynamic ID isn't doing anything yet, we'll soon be adding functionality so that we can click on a contact and get additional information based on its ID.

After following along, our 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[contact.id] = contact;
};

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

AddressBook.prototype.findContact = function(id) {
  if (this.contacts[id] != undefined) {
    return this.contacts[id];
  }
  return false;
};

AddressBook.prototype.deleteContact = function(id) {
  if (this.contacts[id] === undefined) {
    return false;
  }
  delete this.contacts[id];
  return true;
};

// 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 ---------
let addressBook = new AddressBook();

function displayContactDetails(addressBookToDisplay) {
  let contactsList = $("ul#contacts");
  let htmlForContactInfo = "";
  Object.keys(addressBookToDisplay.contacts).forEach(function(key) {
    const contact = addressBookToDisplay.findContact(key);
    htmlForContactInfo += "<li id=" + contact.id + ">" + contact.firstName + " " + contact.lastName + "</li>";
  });
  contactsList.html(htmlForContactInfo);
}

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

Best Practices Review

Let's recap some of the best practices we just used:

  1. We created a separate UI function instead of adding the code to our form submission block. This allows us to focus on writing one function at a time, and helps keep code modular.

  2. We store a jQuery selector inside a variable. That way, if we need to use the selector multiple times, jQuery only needs to query the DOM once, making code faster and more efficient.

  3. We create a list of all elements we want to append to the DOM, and add them all at once instead of one a time. This is also faster and more efficient.

You aren't expected to master these best practices just yet, but continually practice integrating them in your code throughout the course. These kinds of details separate beginning coders from more experienced ones.

In addition to these best practices, we also applied our new knowledge of looping through object keys.

In the next lesson, we'll add UI functions that will allow us to display the detailed information of an individual contact onscreen.


Example GitHub Repo for the Address Book

Best Practices


  1. Create a separate UI function instead of adding the code to the form submission block. This allows us to focus on writing one function at a time, and helps keep code modular.

  2. Store jQuery selectors inside a variable. That way, if we need to use the selector multiple times, jQuery only needs to query the DOM once, making code faster and more efficient.

  3. Create a list of all elements to append to the DOM, and add them all at once instead of one a time. This is also faster and more efficient.

Examples


Create a UI function to display contacts in an address book:

function displayContactDetails(addressBookToDisplay) {
  let contactsList = $("ul#contacts");
  let htmlForContactInfo = "";
  Object.keys(addressBookToDisplay.contacts).forEach(function(key) {
    const contact = addressBookToDisplay.findContact(key);
    htmlForContactInfo += "<li id=" + contact.id + ">" + contact.firstName + " " + contact.lastName + "</li>";
  });
  contactsList.html(htmlForContactInfo);
}

Example GitHub Repo for the Address Book

Lesson 17 of 23
Last updated October 21, 2021