Update Inventory Information (Step 2)

Updating is a Two-Step Process

An update requires two steps: 1) Once it is determined an update is occurring, the information to be changed has to be requested from the database and made available to be edited; 2) When the edit is completed, the data must then be stored back into the database and the user informed of the success or failure of the change. This document covers step 2: Making changes to the data, submitting it to the server, checking the data and ultimately updating the data to the database and notifying the site visitor of the outcome. Step 1 in the update process was covered in the Step 1 activity.

Video Demonstration

The video provides a general overview of the activity, but does not contain the detail needed to complete each process. Watch the video to obtain a general idea, but follow the written steps to complete the activity. This is the Transcript of the video.

The Update View

The view where the data will be modified is nearly complete. The form inputs should display the data that was queried from the database.

What is equally important is that the view should already have the means to display error messages and be "sticky" if incomplete data is submitted to the controller and is returned for correction.

When the data was inserted into the form for editing, what was not included was the inventory id. This is a critical value that will be needed to actually do the update in the database. Let's include it into the form.

  1. Open the view where the update inventory form is located.
  2. Scroll to the bottom of the form and locate the "submit" button or input.
  3. Below the "submit" button or input add an empty line.
  4. In the empty line, add a hidden input containing the name - value pair, which looks like this:
    <input type="hidden" name="inv_id"
    <% if(locals.inv_id) { %> value="<%= locals.inv_id %>"
    <% } %>>
  5. This allows us to submit the primary key for the item without it being obvious to the client looking at the form. In addition, if the page is included for error correction the value of the primary key (from the error check process) is returned and re-inserted into the hidden field.
  6. Ensure that there are no warnings or errors.
  7. Save the view.

The Inventory Route

As with all of our processes, there needs to be an appropriate route handler to watch for and direct the incoming request to the controller for processing. Let's make sure that route is present.

  1. Find and open the inventory route file in the routes folder.
  2. Add a new route with comment to handle the incoming request, like so:
    router.post("/update/", invController.updateInventory)
  3. Notice that "update" matches the value in the form's action attribute.
  4. The error handler is not shown in the example above, be sure you add it to your route!
  5. The controller method does not exist, but we will create it in a few minutes.
  6. Ensure there are no warnings or errors.
  7. Save the file.

Data Checking Middleware

The data incoming from the update process should meet the same requirements as the original data during the "add" process. But, we are now also passing the inv_id and if there are errors, they need to be redirected to the "edit" view, not the "add" view. So, some modifications are in order.

  1. Find and open the "inventory-validation" file in the "utilities" folder.
  2. Copy the "checkInventoryData" function and its comment.
  3. Paste the copied comment and function immediately beneath the just copied function, leaving at least one empty line between the old and new functions.
  4. Change the comment to reflect that errors will be directed back to the edit view.
  5. Change the function name to "checkUpdateData".
  6. Add the "inv_id" to the list of variables to hold data from the request body.
  7. Change the render view to the "edit" view for inventory items.
  8. Change the title: to be the same as the title when delivering the edit view in the inventory controller.
  9. Add the "inv_id" to the list of variables being sent back to the view in the data object.
  10. Make sure no warnings or errors exist in the file.
  11. Save the file.

With the file changed, add the "newInventoryRules()" function and the "checkUpdateData" middleware to the "update" route in the inventoryRoute file. Similar to the middleware used in the "add new inventory" route.

The Inventory Controller

As with all other processes, the controller is responsible for the logic to carry out the update for the data sent from the view and report the result back to the client. Because much of this process is similar to that of adding an item to inventory, we will do some copy and paste to make this process go faster:

  1. Open the inventory controller.
  2. Locate and copy the function used to insert a new inventory item, including the comment.
  3. Now, find the function which queried the data from the database and built the edit view. Click below it, creating a few empty lines, if needed.
  4. Paste the copied code into the second empty line. Be sure there is an empty line between the previous function and the newly pasted function.
  5. With the new function in place, let's edit it to not "Add" a new item, but to carry out the "Update" of the item, like this:
/* ***************************
 *  Update Inventory Data
 * ************************** */
invCont.updateInventory = async function (req, res, next) {
  let nav = await utilities.getNav()
  const {
    inv_id,
    inv_make,
    inv_model,
    inv_description,
    inv_image,
    inv_thumbnail,
    inv_price,
    inv_year,
    inv_miles,
    inv_color,
    classification_id,
  } = req.body
  const updateResult = await invModel.updateInventory(
    inv_id,  
    inv_make,
    inv_model,
    inv_description,
    inv_image,
    inv_thumbnail,
    inv_price,
    inv_year,
    inv_miles,
    inv_color,
    classification_id
  )

  if (updateResult) {
    const itemName = updateResult.inv_make + " " + updateResult.inv_model
    req.flash("notice", `The ${itemName} was successfully updated.`)
    res.redirect("/inv/")
  } else {
    const classificationSelect = await utilities.buildClassificationList(classification_id)
    const itemName = `${inv_make} ${inv_model}`
    req.flash("notice", "Sorry, the insert failed.")
    res.status(501).render("inventory/edit-inventory", {
    title: "Edit " + itemName,
    nav,
    classificationSelect: classificationSelect,
    errors: null,
    inv_id,
    inv_make,
    inv_model,
    inv_year,
    inv_description,
    inv_image,
    inv_thumbnail,
    inv_price,
    inv_miles,
    inv_color,
    classification_id
    })
  }
}
  1. I hope by this point in the semester that as you look at the code you understand what each step in the process does. Take a minute and go down through the new function and explain it to yourself. Better yet, find another person and explain it to them. If you can explain the code, you probably understand it and can move on. If not, ask someone else in your learning team about the line or lines that you do not understand.
  2. Ensure that there are no warnings or errors.
  3. Save the file.

Inventory Model

Finally we will create the updateInventory() function and then be ready to test. I hope you have already guessed that we will create this function by copying and editing your "add inventory" function, since they are so similar.

  1. Find and open the inventory model file.
  2. Locate your add new inventory function, copy it, including the comment.
  3. Move to the bottom of the model file (below the existing functions).
  4. Add a few empty lines between the last existing function and the module.exports statement.
  5. Paste the add new inventory function, leaving an empty line between the existing function and the newly pasted function.
  6. Change the name of the function to updateInventory().
  7. Edit the function to look similar to this Be sure that all indications of "add" are changed to "update"!:
/* ***************************
 *  Update Inventory Data
 * ************************** */
async function updateInventory(
  inv_id,
  inv_make,
  inv_model,
  inv_description,
  inv_image,
  inv_thumbnail,
  inv_price,
  inv_year,
  inv_miles,
  inv_color,
  classification_id
) {
  try {
    const sql =
      "UPDATE public.inventory SET inv_make = $1, inv_model = $2, inv_description = $3, inv_image = $4, inv_thumbnail = $5, inv_price = $6, inv_year = $7, inv_miles = $8, inv_color = $9, classification_id = $10 WHERE inv_id = $11 RETURNING *"
    const data = await pool.query(sql, [
      inv_make,
      inv_model,
      inv_description,
      inv_image,
      inv_thumbnail,
      inv_price,
      inv_year,
      inv_miles,
      inv_color,
      classification_id,
      inv_id
    ])
    return data.rows[0]
  } catch (error) {
    console.error("model error: " + error)
  }
}
  1. The biggest change to the function will be the SQL statement. We have to change it to an UPDATE from an INSERT.
  2. It is absolutely critical that the order of the variables containing the values in the array, match the order of the placeholders in the SQL query. Note that the "inv_id" is last in the array, because it is the last placeholder, used in the WHERE clause.
  3. Add the function to the module.exports list at the bottom of the file.
  4. Ensure that no warnings or errors exist in the file.
  5. Save the file.

Test One

Double check that all files worked with during this activity (inventory route, inventory controller, inventory model, update inventory view) have no errors.

  1. Make sure the development servers are running.
  2. Open the project application in a browser.
  3. Navigate to the login view and login as an employee or administrator.
  4. Navigate to the inventory management view.
  5. Select a classification from the select list.
  6. Click the "Modify" link for one of the items.
  7. The item information should load into the form in the update view.
  8. Right-click on the page and do a "View Page Source".
  9. Look at the code and find the "hidden" input where the inv_id value of the inventory item should be stored. Make sure it is there.
  10. Return to the form and change something, just one thing for right now.
  11. Submit the form.
  12. If things worked you should be returned to the inventory management view, and you should see the success message.
  13. To confirm the change, you can do one of two things:
    • Bring up the same item in the management view and click the "modify" link for the item that was just updated. Does the change appear in the input?
    • Click the navigation bar item for the classification that included the item that was just changed. Click the item to see the details. Does the change appear in the detail view?
  14. If everything worked, do a fist pump over your head and feel proud of yourself. If not, review your code and do some troubleshooting, get help from a learning team member or someone else.

Test Two

  1. Click the "modify" option for an inventory item. In the update view, submit the form again, but without making a change.
  2. You should be returned to the management view with a success message.
  3. While this does no harm, it takes time and uses server resources that need not be used.
  4. Let's fix this.

A JavaScript Function

We will write a small JavaScript function and attach it to the edit-inventory view. This function will not allow the "Update" button from executing unless some data has changed.

  1. Find and click the public > js folder to select it.
  2. Click the "New File" icon and create a new JavaScript file, named inv-update.js.
  3. Add this code to the file, then save.
const form = document.querySelector("#updateForm")
    form.addEventListener("change", function () {
      const updateBtn = document.querySelector("button")
      updateBtn.removeAttribute("disabled")
    })
  1. Open the edit-inventory view.
  2. Add an id to the opening form tag with the value of "updateForm".
  3. Add the "disabled" attribute to the button.
  4. Link the remote JavaScript file containing the function you just wrote to the view using a <script> element and remember to NOT include the public folder in the file path.
  5. If you used an <input type="submit"> in the form, rather than a <button>, then adjust the JavaScript function to work with it.
  6. Save the view.

Test Three

  • Ensure the server is running.
  • Go through the process to select an inventory item to edit.
  • When the edit-inventory view loads, the submit button should be disabled.
  • Change something in the form. Anything.
  • The Submit button should become enabled after the change.
  • If everything worked - Congratulations (Crowd cheering in the background)!
  • If not, troubleshoot and get help until it is operational.
  • Stop the servers.
  • While the client-side JavaScript can be turned off, this is an easy solution to prevent submitting the form if no data is changed in the form.