Iterating by adding an image removal capability

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:

  • Update routes.js to include a new route to handle Delete requests
  • Update controllers/image.js to include a new function for the route

    This should not only remove the image from the database, but also delete the file and all related comments

  • Update the image.handlebars HTML template to include a Remove button
  • Update the public/js/scripts.js file with an AJAX handler for the Remove button

Adding a route

The 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);

Adding a controller handler

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.

Updating the Handlebars image page template

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).

Updating the jQuery

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.

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

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