Lesson 20. Updating and Deleting your Models

In lesson 19, you built create and read functionality for your models. Now it’s time to complete the CRUD methods. In this lesson, you add the routes, actions, and views for the update and delete functions. First, you create a form to edit the attributes of existing users. Then you manage the modified data in an update action. At the end of the lesson, you implement a quick way to delete users from your users index page. To start, make sure that your MongoDB server is running by entering mongod in a terminal window.

This lesson covers

  • Constructing a model edit form
  • Updating user records in your database
  • Deleting user records
Consider this

Your recipe application is ready to accept new users, but you’re getting complaints that multiple unnecessary accounts were made and that some users accidentally typed the wrong email address. With the update and delete CRUD functions, you’ll be able to clear unwanted records and modify existing ones to persist in your application.

20.1. Building the edit user form

To update a user’s information, you use some Mongoose methods in a specific update action. First, though, you create a form to edit user information. The form looks like the one in create.js, but the form’s action points to users/:id/update instead of users/create because you want your route to indicate that the form’s contents are updating an existing user, not creating a new one.

You also want to replace the values in each form input with the user’s existing information. The input for the user’s first name might look like the next listing, for example. The value attribute here uses the existing user’s first name. This code works only if a user object is being passed into this page.

Listing 20.1. Input example with user’s data in edit.ejs
<input type="text" name="first" id="inputFirstName" value="<%=
 user.name.first %>" placeholder="First" autofocus>               1

  • 1 Apply the existing user’s attribute values in edit form.

To ensure that an existing user’s data populates this form, add another column to the table in the users index page. Your index page should resemble figure 20.1.

Figure 20.1. Users index page with edit links in your browser

This column has a link for editing each specific user. You can add an anchor tag, as shown in the next listing. The href value for the edit link tag makes a GET request to the /users plus the user’s Id plus /edit route.

Listing 20.2. Modified table with link to edit users in index.ejs
<td>
  <a href="<%=`/users/${user._id}/edit` %>">
    Edit
  </a>
</td>            1

  • 1 Embed the user’s ID in the edit tag link.

Next, you want to modify the form in edit.ejs to submit a PUT request with modified user data, but your HTML form element supports only GET and POST requests. It’s important to begin using the intended HTTP methods with your CRUD functions so that there’s no future confusion about whether a request is adding new data or modifying existing data.

One problem you need to address is how Express.js will receive this request. Express.js receives your HTML form submissions as POST requests, so you need some way to interpret the request with the HTTP method you intended. Several solutions to this problem exist. The solution you use in this lesson is the method-override package.

method-override is middleware that interprets requests according to a specific query parameter and HTTP method. With the _method=PUT query parameter, you can interpret POST requests as PUT requests. Install this package by running npm i method-override -S in your project’s terminal window, and add the lines in listing 20.3 to main.js.

First, require the method-override module into your project. Tell the application to use methodOverride as middleware. Specifically, you’re telling this module to look for the _method query parameter in the URL and to interpret the request by using the method specified as the value of that parameter. A POST request that you want processed as a PUT request, for example, will have ?_method=PUT appended to the form’s action path.

Listing 20.3. Adding method-override to your application in main.js
const methodOverride = require("method-override");     1
router.use(methodOverride("_method", {
  methods: ["POST", "GET"]
}));                                                   2

  • 1 Require the method-override module.
  • 2 Configure the application router to use methodOverride as middleware.

You want to modify the form in edit.ejs to submit the form with a POST method to the /users/:id/update?_method=PUT route. The opening form tag will look like listing 20.4.

The action is dynamic, depending on the user’s ID, and points to the /users/:id/update route. Your method-override module interprets the query parameter and helps Express.js match the request’s method with the appropriate route.

Listing 20.4. Pointing the edit form to the update route in edit.ejs
<form method="POST" action="<%=`/users/${user._id}/update
 ?_metho d=PUT`%>">                                          1

  • 1 Add a form to update user data.

You can reference the complete user edit form in the next listing, which should look like figure 20.2 in your browser for an existing user.

Figure 20.2. User edit page in your browser

Listing 20.5. Complete user edit form in edit.ejs
<div class="data-form" >                                        1
  <form method="POST" action="<%=`/users/${user._id}/update
 ?_method=PUT`%>">
    <h2>Edit user:</h2>
    <label for="inputFirstName">First Name</label>
    <input type="text" name="first" id="inputFirstName" value="<%=
 user.name.first %>" placeholder="First" autofocus>
    <label for="inputLastName">Last Name</label>
    <input type="text" name="last" id="inputLastName" value="<%=
 user.name.last %>" placeholder="Last">
    <label for="inputPassword">Password</label>
    <input type="password" name="password" id="inputPassword"
 value="<%= user.password %>" placeholder="Password" required>
    <label for="inputEmail">Email address</label>
    <input type="email" name="email" id="inputEmail" value="<%=
 user.email %>" placeholder="Email address" required>
    <label for="inputZipCode">Zip Code</label>
    <input type="text" name="zipCode" id="inputZipCode"
 pattern="d*" value="<%= user.zipCode %>" placeholder="Zip
 Code" required>
    <button type="submit">Update</button>
  </form>
</div>

  • 1 Display the user edit form.

In the next section, you add the routes and actions that get this form to work, as well as the data from the form processed.

Quick check 20.1

Q1:

Why do you use the PUT method for the edit form and POST for the new form?

QC 20.1 answer

1:

The edit form is updating data for an existing record. By convention, the request to submit data to your server should use an HTTP PUT method. To create new records, use POST.

 

20.2. Updating users from a view

Now that the user edit form is in its own view, add the controller action and route to complement the form. The edit route and action send users to view edit.ejs. The update route and action are used internally to make changes to the user in the database. Then the redirectView action acts as the action following update, redirecting you to a view that you specify. Add the actions in listing 20.6 to usersController.js.

The edit action, like the show action, gets a user from the database by the user’s ID and loads a view to //edit the user. Notice that if a user isn’t found by the ID parameter, you pass an error to the error-handling middleware function. The update action is called when the edit form is submitted; like the create action, it identifies the user’s ID and userParams, and passes those values into the Mongoose findByIdAndUpdate method. This method takes an ID followed by parameters you’d like to replace for that document by using the $set command. If the user updates successfully, redirect to the user’s show path in the next middleware function; otherwise, let the error-handling middleware catch any errors.

Listing 20.6. Adding edit and update actions to usersController.js
edit: (req, res, next) => {                      1
  let userId = req.params.id;
  User.findById(userId)                          2
      .then(user => {
        res.render("users/edit", {
          user: user
        });                                      3
      })
      .catch(error => {
        console.log(`Error fetching user by ID: ${error.message}`);
        next(error);
      });
},

update: (req, res, next) => {                    4
  let userId = req.params.id,
    userParams = {
      name: {
        first: req.body.first,
        last: req.body.last
      },
      email: req.body.email,
      password: req.body.password,
      zipCode: req.body.zipCode
    };                                           5

  User.findByIdAndUpdate(userId, {
    $set: userParams
  })                                             6
      .then(user => {
        res.locals.redirect = `/users/${userId}`;
        res.locals.user = user;
        next();                                  7
      })
      .catch(error => {
        console.log(`Error updating user by ID: ${error.message}`);
        next(error);
      });
}

  • 1 Add the edit action.
  • 2 Use findById to locate a user in the database by their ID.
  • 3 Render the user edit page for a specific user in the database.
  • 4 Add the update action.
  • 5 Collect user parameters from request.
  • 6 Use findByIdAndUpdate to locate a user by ID and update the document record in one command.
  • 7 Add user to response as a local variable, and call the next middleware function.

Last, you need to add the routes in listing 20.7 to main.js. The path to edit a user is a straightforward route with an id parameter. The POST route to update the user from the edit form follows the same path structure but uses the update action. You’re also going to reuse the redirectView action to display the view specified in your response’s locals object.

Listing 20.7. Adding edit and update routes to main.js
router.get("/users/:id/edit", usersController.edit);         1
router.put("/users/:id/update", usersController.update,
 usersController.redirectView);                           2

  • 1 Add routes to handle viewing.
  • 2 Process data from the edit form, and display the user show page.

Relaunch your application, visit the users index page, and click the edit link for a user. Try to update some values, and save.

With the ability to create, read, and update user data, you’re missing only a way to remove records that you don’t want anymore. The next section covers the delete function.

Quick check 20.2

Q1:

True or false: findByIdAndUpdate is a Mongoose method.

QC 20.2 answer

1:

True. findByIdAndUpdate is a Mongoose method used to make your query more succinct and readable in your server’s code. The method can’t be used unless the Mongoose package is installed.

 

20.3. Deleting users with the delete action

To delete a user, you need only one route and a modification to your users index page. In index.ejs, add a column titled delete. As you did with the edit column, link each user to a users/:id/delete route (figure 20.3).

Figure 20.3. Users index page with delete links in your browser

Note

You can add some basic security with an HTML onclick="return confirm('Are you sure you want to delete this record?')"

Recall that you need to use the _method=DELETE query parameter so that your application can interpret GET requests as DELETE requests. Add the code for the delete column in the users index page, as shown in listing 20.8. With the appended query parameter to send a DELETE request, this link passes the user’s ID in search of an Express.js route handling DELETE requests. The confirmation script displays a modal to confirm that you want to submit the link and delete the record.

Listing 20.8. Delete link in users index.ejs
<td>
  <a href="<%= `users/${user._id}/delete?_method=DELETE` %>"
 onclick="return confirm('Are you sure you want to delete
 this record?')">Delete</a>                                  1
</td>

  • 1 Add a link to the delete action on the index page.

Next, add the controller action to delete the user record by its ID. Add the code in listing 20.9 to usersController.js.

You’re using the Mongoose findByIdAndRemove method to locate the record you clicked and remove it from your database. If you’re successful in locating and removing the document, log that deleted user to the console and redirect in the next middleware function to the users index page. Otherwise, log the error as usual, and let your error handler catch the error you pass it.

Listing 20.9. Adding the delete action to usersController.js
delete: (req, res, next) => {
  let userId = req.params.id;
  User.findByIdAndRemove(userId)              1
      .then(() => {
        res.locals.redirect = "/users";
        next();
      })
      .catch(error => {
        console.log(`Error deleting user by ID: ${error.message}`);
        next();
      });
}

  • 1 Deleting a user with the findByIdAndRemove method

The only missing piece is the following route, which you add to main.js: router.delete ("/users/:id/delete", usersController.delete, usersController.redirectView). This route handles DELETE requests that match the path users/ plus the user’s ID plus /delete. Then the route redirects to your specified redirect path when the record is deleted.

Try this new code by running the application again and visiting the users index page. Click the delete link next to one of the users, and watch it disappear from your page.

Last, to make it easier to use your new CRUD actions from a user’s profile page, add the links in the following listing to the bottom of show.ejs.

Listing 20.10. Adding links for user CRUD actions to show.ejs
<div>
  <a href="/users">View all users</a>
</div>
<div>
  <a href="<%=`/users/${user._id}/edit`%>">
    Edit User Details
  </a>
</div>
<div>
  <a href="<%= `/users/${user._id}/delete?_method=DELETE` %>"
  onclick="return confirm('Are you sure you want to delete
  this record?')">Delete</a>
</div>                              1

  • 1 Add links to edit and delete a user’s account from their profile page.

The user’s show page should resemble figure 20.4.

Figure 20.4. User’s show page with links to edit and delete

Quick check 20.3

Q1:

Why is ?_method=DELETE needed at the end of your link’s path?

QC 20.3 answer

1:

method-override looks for the _method query parameter and its mapped method. Because you’re using this package to filter incoming GET and POST requests as alternative methods, you need to append this parameter and value.

 

Summary

In this lesson, you learned how to edit records and delete records from your database. You also saw how to use the method-override package to assist with HTML limitations in submitting certain request methods. With your CRUD functionality complete, it’s time to build an application with associated models and a user interface to save meaningful data to your database. In the next capstone exercise (lesson 21), try to apply everything you’ve learned in this unit to build the Confetti Cuisine application.

Try this

Now that you have each CRUD function working for user accounts, make sure that the same setup is in place for groups and subscribers. Before you move on to the capstone exercise (lesson 21), make sure that all three models have working index, new, edit, and show pages. Then, as in lesson 19, try to incorporate associated models into each record’s show page.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset