At this point, I think our application is pretty awesome, but there's something missing that's nagging me. During testing, I've been creating all kinds of new images and uploading them to the application but it's starting to get a bit cluttered and messy. It dawned on me that the most obvious thing that's missing is the ability to remove an image!
In reality, I left out this feature on purpose so that we could use this opportunity to incorporate a completely new set of functionality that touches almost every area of the application. This seemingly simple addition is actually going to require the following changes:
routes.js
to include a new route to handle Delete
requestscontrollers/image.js
to include a new function for the routeThis should not only remove the image from the database, but also delete the file and all related comments
image.handlebars
HTML template to include a Remove buttonpublic/js/scripts.js
file with an AJAX handler for the Remove buttonThe first thing we need to update in order to add this new functionality is the main routes
list. Here we will add a new endpoint that handles DELETEs and points to a function within the image controller. Edit the server/routes.js
file and insert the following new line of code:
app.delete('/images/:image_id', image.remove);
Now that we have added a new route, we need to create the controller function that it's using as its callback (image.remove
). Edit controllers/image.js
and add the following new function code after the existing comment: function(req, res){}
operation (don't forget to add a trailing comma after the comment function since you are adding a new function):
remove: function(req, res) { Models.Image.findOne({ filename: { $regex: req.params.image_id } }, function(err, image) { if (err) { throw err; } fs.unlink(path.resolve('./public/upload/' + image.filename), function(err) { if (err) { throw err; } Models.Comment.remove({ image_id: image._id}, function(err) { image.remove(function(err) { if (!err) { res.json(true); } else { res.json(false); } }); }); }); }); }
This function performs four primary functions (and as such nests four layers deep with callbacks—we could have used async
's series
method here to prevent the crazy amount of nesting). The first task is to find the image that is attempting to be removed. Once that image is found, the file associated with the image should be deleted. Next, find the comments associated with the image and remove those. Once they have finished being removed, the last step is to remove the image itself. Assuming all of that was a success, simply send a true
Boolean JSON response back to the browser.
Now that we have a route and controller function to support deleting an image, we need a way for the UI to send the request. The most obvious solution is to just add a Delete button somewhere on the page. Edit the views/image.handlebars
file and after the existing HTML, where we had the Like button, we are going to add new HTML for a Delete button:
<div class="col-md-8"> <button class="btn btn-success" id="btn-like" ... // existing HTML for Like button and misc details </div> <div class="col-md-4 text-right"> <button class="btn btn-danger" id="btn-delete" data-id="{{ image.uniqueId }}"> <i class="fa fa-times"></i> </button> </div>
Here we just include a new div
that's set to four columns using Bootstrap and right aligned. The UI here is that the Like button and stats are the left-most portion of the row, and the Delete button (an X icon from Font Awesome) is all the way to the right of the same row (and red since we use Bootstrap's danger color class).
Finally, we are going to tie it all together by implementing code similar to the Like button, where we send an AJAX delete to the server with the URL and the image ID when the button is clicked on. To be safe, we display a standard JavaScript confirmation dialog to ensure the button wasn't clicked by accident.
Assuming the server responds with a true value, we will turn the button green and change the icon to a checkmark with the word Deleted! in place. Edit public/js/scripts.js
and insert the following block of code after the existing code (be sure to insert the new code inside the $(function(){ ... });
jQuery function):
$('#btn-delete').on('click', function(event) { event.preventDefault(); var $this = $(this); var remove = confirm('Are you sure you want to delete this image?'), if (remove) { var imgId = $(this).data('id'), $.ajax({ url: '/images/' + imgId, type: 'DELETE' }).done(function(result) { if (result) { $this.removeClass('btn-danger').addClass('btn-success'), $this.find('i').removeClass('fa-times').addClass('fa-check'), $this.append('<span> Deleted!</span>'), } }); } });
Let's test out this brand new functionality by launching the application, loading it up in a browser, finding any image we no longer want, and viewing its image page. The Delete button should now show up in place.
3.141.31.125