WDD 330: Web Frontend Development II

W04 Individual Activity: Error Checking and Validation

Overview

In the last activity we got our checkout working at a minimum level. If the user does everything exactly the way you want the to the order will succeed. We cannot rely upon users to do the right thing every time. We need to make sure that the user is doing the right thing. We will do this by adding error checking and validation.

Activity Instructions

Complete the following assignment individually. Each student will write code for their own copy.

Start the task

  1. Visit the team's copy of the Trello board for the project.
  2. Add yourself to the Individual Activity W04: Error Checking and Validation card.
  3. If no one else has done it yet, move it to 'Doing'.
  4. Read the details of the card.
  5. Make sure to pull any changes from GitHub before proceeding.
  6. Create a new branch called yourinitials--individual4.

Figuring out what went wrong

Currently if our order fails we get the very descriptive error message "Bad Request". Turns out that the server is actually sending back much more information than that, but we are throwing it away by how we are processing our response.

  1. Open the ExternalServices.mjs module and take a look at the convertToJson function. It should look something like this:
    async function convertToJson(res) {
      if (res.ok) {
        return res.json();
      } else {
        throw new Error('Bad Response');
      }
    }
    • On line 1 we check our response to see if it is 'ok' (res.ok). 'Ok' is any status of 200, or most 300s.
    • On line 2 we return the response as json.
    • On line 4 we throw an error if the response is not ok. If the status on the response is 40x or 50x then it is NOT okay. The server we are communicating with sends back a 400 response if something goes wrong. Then it puts the details in the body of the response.

    What we need to do is get the detailed error message out of the response body.

  2. Refactor this function by converting our response body to JSON before we check if it's okay.
  3. Then if it is not okay then send the response body on in our throw statement.
    throw new Error() is a nice way to let the application know that something went wrong. In this case, there is a problem with it because Error() takes a simple string as an argument. We can't send an object. There are a couple of ways to get around this.
    1. We could stringify the object.
    2. We could send a custom object back with our throw instead of the default new Error().
    In this case the second method makes more sense.
  4. Use the Error() object's name and message properties to send the response body back to the calling function. Change the line that looks like throw new Error('Bad Response'); to something more like this:
    throw { name: 'servicesError', message: jsonResponse };
    The variable jsonResponse should be the body of the response from the server that we converted to JSON.

Catching the error

Throwing an error when something goes wrong does little good if we don't catch it somewhere and deal with it.

In this case there are a couple of options for where to place our catch. We could place it in the checkout method that we wrote in the ExternalServices module, or we could place it in the checkout method of the CheckoutProcess module. Think about the pros and cons of putting it in each place.

Where to put the error handling

Handling an error close to where it happened is usually a good thing, but we need to let the user know what they did wrong in this case. The form and form handling is in the CheckoutProcess module, and so in this case it makes sense to catch the error there.

  1. Add a try { } catch(err) { } block in the CheckoutProcess->checkout.
    • The part of the code that we are worried about breaking should go inside try { // code here }
    • And whatever you want to happen if the code in the try has an error should go in the catch(err) { }

Form Validation

One of the things that the server will complain about is if any of the fields are missing information. HTML can actually do a really nice job of checking for that. All we need to do is add required as an attribute of an input element.

  1. Add the HTML required attribute to all the form inputs.

Depending on how you are listening for the form submission your HTML validation may or may not be working. If you attached a click listener on the button we will need to trigger the validation ourselves, if you listened for the submit event on the form the validation should automatically be working. Previously, we determined that since we were submitting our form through AJAX, we did not want the form to do what it normally would do if submitted. In the listener for the form button you have added e.preventDefault();. That part of your code should look something like this:

document.querySelector('#checkoutSubmit')
.addEventListener('click', (e) => {
  e.preventDefault();
  myCheckout.checkout();
});

Do the following in your listener handler function if the form validation is not working:

  1. Get the form from the DOM.
  2. Forms have a method called checkValidity that will return false if it finds anything in the form data that goes against our validation rules. Call it and store the result into a variable.
  3. We can also manually trigger the messages that the browser will add to the page when something is wrong. That is done with myForm.reportValidity()
  4. Finally, if our validity test came back false, we do not want to continue.
    Wrap myCheckout.checkout(); in an if statement to stop it from continuing.
Click for example
document.querySelector('#checkoutSubmit').addEventListener('click', (e) => {
  e.preventDefault();
  const myForm = document.forms[0];
  const chk_status = myForm.checkValidity();
  myForm.reportValidity();
  if(chk_status)
    myCheckout.checkout();
  });

By leveraging the built in HTML validation that the browser can do, we dramatically reduced the amount of error handling we will need to do on our end!

Thinking back to the reading from earlier (MDN: Client side form validation), what else should we add to our HTML validation to help our users to give us good input? Plan on discussing this with your team and add those things you think appropriate.

Handling the Happy Path 😁

Currently our application does not give any feedback to the user about whether the order succeeded or failed. Let's start by responding to a successful order. We should probably do the following:

☑️ Give the user a success message.
☑️ Clear out the cart.
☑️ Anything else?

This is not a very long list. The main decision to be made is how do we want to message the user? We could either pop something up in the current page, or take the user to a new page with a success message. Leaving them on the page with the form does not make a lot of sense. Let's move to a success page.

  1. Inside of the checkout directory create a new file: success.html.
  2. Add our base formatting including header and footer, and then add a success message for the user.
  3. Then add the code to your checkout process to take the user to this page upon a successful checkout.
  4. Make sure to also clear out our cart contents in localStorage.

Stretch Goals

Handling the Unhappy Path 😢

The last thing we need to do is to handle the few last errors the user might get. We want to stay on the checkout form page so the user does not lose what they have already entered and so they can know what went wrong and fix it if possible. So a popup message would be appropriate here.

'popup' does not refer to an alert. We want something much less intrusive that will not hijack the UI.

Creating custom alert messages is something that could be useful outside of errors, so a new function in our utilities module sounds appropriate.

  1. Create and export a function called alertMessage(message, scroll=true) in utils.mjs.
  2. Create an alert that matches the screenshot below.
  3. Insert your alert at the top of the main element.
  4. Since the checkout form is longer than will fit on the screen of a mobile device, your alertMessage should have the option to scroll the page back to the top to make sure the user sees the error messages.

Example screenshot of an alert message above the form.
Click for example
export function alertMessage(message, scroll = true) {
  // create element to hold our alert
  const alert = document.createElement('div');
  // add a class to style the alert
  alert.classList.add('alert');
  // set the contents. You should have a message and an X or something the user can click on to remove

  // add a listener to the alert to see if they clicked on the X
  // if they did then remove the child
  alert.addEventListener('click', function(e) {
      if( ) { // how can we tell if they clicked on our X or on something else?  hint: check out e.target.tagName or e.target.innerText
        main.removeChild(this);
      }
  })
  // add the alert to the top of main
  const main = document.querySelector('main');
  main.prepend(alert);
  // make sure they see the alert by scrolling to the top of the window
  //we may not always want to do this...so default to scroll=true, but allow it to be passed in and overridden.
  if(scroll)
    window.scrollTo(0,0);
}

Add an alert to Product detail

When you add a product to the cart there is a good chance that the action gives no feedback to the user. If you have already fixed this and yours does give feedback, Kudos! If not, we can use this utility alert to quickly send the user a message that the item was successfully added.

Instructor's Solution

As a part of this individual activity, you are expected to look over a solution from the instructor, to compare your approach to that one. One of the questions on the I-Learn submission will ask you to provide insights from this comparison.

Do NOT open the solution until you have worked through this activity for at least one hour. At the end of the hour, if you are still struggling with some of the core requirements, you are welcome to view the instructor's solution and use it to help you complete your own code.

After working individually for the one hour activity, click here for the instructor's solution.

Make a Pull Request

Wrap up this individual project work by finishing up your individual work and then coordinating with your team.

  1. Commit and push your changes.
  2. Meet with your team and discuss which team member's changes (branch) you would like to keep.
    Your team will only keep one.
  3. Submit a request for the branch that you decided to keep.
  4. Review the Pull Request, close it, and merge the branch back into the Main branch.
  5. Move the task to the "Done" column.

Submission

Return to I-Learn to report on your work.