Lesson Wednesday

Now, let's add user interface logic that will collect address details for each contact that we create. We'll want to be able to display a contact's address information to the screen when the link for their name is clicked.

Here are the three HTML items we'll need to complete to do this:

  1. Form input fields so that we can collect the data for the properties we defined in our Address constructor: street, city, and state.
  2. A button with the label, "Another address", so our users can add more than one address to a contact.
  3. A place to display a contact's addresses in our show-contact div on the right side of the screen.

Here are the HTML updates that add these three elements:

address-book.html
<!DOCTYPE html>
<html>
  <head>
    <link href="css/bootstrap.css" rel="stylesheet" type="text/css">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="js/jquery-1.12.0.js"></script>
    <script src="js/scripts.js"></script>
    <title>Address book</title>
  </head>
  <body>
    <div class="container">
      <h1>Address book</h1>

      <div class="row">

        <div class="col-md-6">
          <h2>Add a contact:</h2>
          <form id="new-contact">
            <div class="form-group">
              <label for="new-first-name">First name</label>
              <input type="text" class="form-control" id="new-first-name">
            </div>
            <div class="form-group">
              <label for="new-last-name">Last name</label>
              <input type="text" class="form-control" id="new-last-name">
            </div>

            <div id="new-addresses">
              <div class="new-address">
                <div class="form-group">
                  <label for="new-street">Street</label>
                  <input type="text" class="form-control new-street">
                </div>
                <div class="form-group">
                  <label for="new-city">City</label>
                  <input type="text" class="form-control new-city">
                </div>
                <div class="form-group">
                  <label for="new-state">State</label>
                  <input type="text" class="form-control new-state">
                </div>
              </div>
            </div>

            <span class="btn btn-primary" id="add-address">Another address</span>

            <button type="submit" class="btn">Add</button>
          </form>

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

          </ul>
        </div>

        <div class="col-md-6">
          <div id="show-contact">
            <h2></h2>
            <p>First name: <span class="first-name"></span></p>
            <p>Last name: <span class="last-name"></span></p>
            <p>Addresses:</p>
            <ul id="addresses">

            </ul>
          </div>
        </div>

      </div>
    </div>
  </body>
</html>

GitHub addressBook repo with updated html

Now we have fields to collect address data for street, city and state and a place to display them. We have the button to allow a user to add multiple addresses but without any code to listen for when it is clicked, it doesn't have any use.

JQuery click listener

Let's add some jQuery to our application that will show new fields for another address when the user clicks it.

Note that this jQuery code must be added in the document ready callback function, but it should not go inside of the form submit listener callback function. We want the button to be functional before we submit the form.

js/scripts.js
$(document).ready(function() {
  $("#add-address").click(function() {
    $("#new-addresses").append('<div class="new-address">' +
                                 '<div class="form-group">' +
                                   '<label for="new-street">Street</label>' +
                                   '<input type="text" class="form-control new-street">' +
                                 '</div>' +
                                 '<div class="form-group">' +
                                   '<label for="new-city">City</label>' +
                                   '<input type="text" class="form-control new-city">' +
                                 '</div>' +
                                 '<div class="form-group">' +
                                   '<label for="new-state">State</label>' +
                                   '<input type="text" class="form-control new-state">' +
                                 '</div>' +
                               '</div>');
  });
...

When appending a large amount of HTML with jQuery, we'll break it into smaller strings on different lines, using the + operator to concatenate them. This makes it more readable than if it was all on a single line. For further readability, we keep the spacing and indentation the same as our other HTML.

GitHub addressBook repo with jQuery to add additional address fields

jQuery form submit listener

Next we need to add some additional jQuery logic to our form submit listener callback function in order to actually collect and use the inputted address data. Previously, when the form was submitted, we collected the first and last name and created a Contact object using that data. Now we must also loop through the address form fields to grab the inputs, create Address objects, and push them onto the contact object's addresses array property.

Here's the code to accomplish this. It should go after the line that creates the new Contact object. Note that you will not yet be able to see the results of this, because we have not yet told our page to display the address data. That's the final step, coming next.

js/scripts.js
...
$(".new-address").each(function() {
  var inputtedStreet = $(this).find("input.new-street").val();
  var inputtedCity = $(this).find("input.new-city").val();
  var inputtedState = $(this).find("input.new-state").val();
  var newAddress = new Address(inputtedStreet, inputtedCity, inputtedState);
  newContact.addresses.push(newAddress);
});
...

Here, we have a new loop: if we want to loop through all of the elements in a jQuery selection, we use the .each() method. It's a lot like forEach() for arrays, but instead of taking a parameter that each element is assigned to, we use the this keyword to refer to the current element.

We also are using the find() method, which will look through all of the child elements of the selected element for any elements that match the criteria passed as an argument. There's a children() method, too, but children() will only traverse down a single level, whereas find() will look at children, their children, and so on. Since our inputs are nested within form-group <div>s, we need to traverse down two levels.

GitHub addressBook repo with jQuery to create Address objects

Displaying the address info

Finally, we need to make sure that, when we display the contact, we loop through all of the addresses and display them in the <ul> with an id of addresses. Here's the code for that piece, which goes inside of the .contact click listener callback function.

js/scripts.js
...
$("ul#addresses").text("");
newContact.addresses.forEach(function(address) {
  $("ul#addresses").append("<li>" + address.street + ", " + address.city + " " + address.state + "</li>");
});
...

GitHub addressBook repo with jQuery to display addresses


Phew! That was a lot to take in.

Here is the jQuery code incorporating all of the additions above. (This also adds in 3 lines at the end to clear the 3 address fields after the form is submitted)

js/scripts.js
...
$(document).ready(function() {

  $("#add-address").click(function() {
    $("#new-addresses").append('<div class="new-address">' +
                                 '<div class="form-group">' +
                                   '<label for="new-street">Street</label>' +
                                   '<input type="text" class="form-control new-street">' +
                                 '</div>' +
                                 '<div class="form-group">' +
                                   '<label for="new-city">City</label>' +
                                   '<input type="text" class="form-control new-city">' +
                                 '</div>' +
                                 '<div class="form-group">' +
                                   '<label for="new-state">State</label>' +
                                   '<input type="text" class="form-control new-state">' +
                                 '</div>' +
                               '</div>');
  });

  $("form#new-contact").submit(function(event) {
    event.preventDefault();

    var inputtedFirstName = $("input#new-first-name").val();
    var inputtedLastName = $("input#new-last-name").val();
    var newContact = new Contact(inputtedFirstName, inputtedLastName);

    $(".new-address").each(function() {
      var inputtedStreet = $(this).find("input.new-street").val();
      var inputtedCity = $(this).find("input.new-city").val();
      var inputtedState = $(this).find("input.new-state").val();
      var newAddress = new Address(inputtedStreet, inputtedCity, inputtedState)
      newContact.addresses.push(newAddress)
    });

    $("ul#contacts").append("<li><span class='contact'>" + newContact.fullName() + "</span></li>");

    $(".contact").last().click(function() {
      $("#show-contact").show();
      $("#show-contact h2").text(newContact.fullName());
      $(".first-name").text(newContact.firstName);
      $(".last-name").text(newContact.lastName);
      $("ul#addresses").text("");
      newContact.addresses.forEach(function(address) {
        $("ul#addresses").append("<li>" + address.street + ", " + address.city + " " + address.state + "</li>");
      });
    });

    $("input#new-first-name").val("");
    $("input#new-last-name").val("");
    $("input.new-street").val("");
    $("input.new-city").val("");
    $("input.new-state").val("");

  });
});

GitHub addressBook repo with code to clear address fields

Sample code

address-book.html
<!DOCTYPE html>
<html>
  <head>
    <link href="css/bootstrap.css" rel="stylesheet" type="text/css">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="js/jquery-1.12.0.js"></script>
    <script src="js/scripts.js"></script>
    <title>Address book</title>
  </head>
  <body>
    <div class="container">
      <h1>Address book</h1>

      <div class="row">

        <div class="col-md-6">
          <h2>Add a contact:</h2>
          <form id="new-contact">
            <div class="form-group">
              <label for="new-first-name">First name</label>
              <input type="text" class="form-control" id="new-first-name">
            </div>
            <div class="form-group">
              <label for="new-last-name">Last name</label>
              <input type="text" class="form-control" id="new-last-name">
            </div>

            <div id="new-addresses">
              <div class="new-address">
                <div class="form-group">
                  <label for="new-street">Street</label>
                  <input type="text" class="form-control new-street">
                </div>
                <div class="form-group">
                  <label for="new-city">City</label>
                  <input type="text" class="form-control new-city">
                </div>
                <div class="form-group">
                  <label for="new-state">State</label>
                  <input type="text" class="form-control new-state">
                </div>
              </div>
            </div>

            <span class="btn btn-primary" id="add-address">Another address</span>

            <button type="submit" class="btn">Add</button>
          </form>

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

          </ul>
        </div>

        <div class="col-md-6">
          <div id="show-contact">
            <h2></h2>
            <p>First name: <span class="first-name"></span></p>
            <p>Last name: <span class="last-name"></span></p>
            <p>Addresses:</p>
            <ul id="addresses">

            </ul>
          </div>
        </div>

      </div>
    </div>
  </body>
</html>
js/scripts.js
...
$(document).ready(function() {

  $("#add-address").click(function() {
    $("#new-addresses").append('<div class="new-address">' +
                                 '<div class="form-group">' +
                                   '<label for="new-street">Street</label>' +
                                   '<input type="text" class="form-control new-street">' +
                                 '</div>' +
                                 '<div class="form-group">' +
                                   '<label for="new-city">City</label>' +
                                   '<input type="text" class="form-control new-city">' +
                                 '</div>' +
                                 '<div class="form-group">' +
                                   '<label for="new-state">State</label>' +
                                   '<input type="text" class="form-control new-state">' +
                                 '</div>' +
                               '</div>');
  });

  $("form#new-contact").submit(function(event) {
    event.preventDefault();

    var inputtedFirstName = $("input#new-first-name").val();
    var inputtedLastName = $("input#new-last-name").val();
    var newContact = new Contact(inputtedFirstName, inputtedLastName);

    $(".new-address").each(function() {
      var inputtedStreet = $(this).find("input.new-street").val();
      var inputtedCity = $(this).find("input.new-city").val();
      var inputtedState = $(this).find("input.new-state").val();
      var newAddress = new Address(inputtedStreet, inputtedCity, inputtedState)
      newContact.addresses.push(newAddress)
    });

    $("ul#contacts").append("<li><span class='contact'>" + newContact.fullName() + "</span></li>");

    $(".contact").last().click(function() {
      $("#show-contact").show();
      $("#show-contact h2").text(newContact.fullName());
      $(".first-name").text(newContact.firstName);
      $(".last-name").text(newContact.lastName);
      $("ul#addresses").text("");
      newContact.addresses.forEach(function(address) {
        $("ul#addresses").append("<li>" + address.street + ", " + address.city + " " + address.state + "</li>");
      });
    });

    $("input#new-first-name").val("");
    $("input#new-last-name").val("");
    $("input.new-street").val("");
    $("input.new-city").val("");
    $("input.new-state").val("");

  });
});