So in the last lesson we looked at how we are able to add new items into our ToDo list by switching
our database from an array over to MongoDB.
In this lesson I want to look at the other functionality that we promise people which is that when
we click on one of these text boxes it should be able to delete an item. At the moment
it doesn't really do that.
It just crosses it out.
But say if we had a really long todo list and we had a whole bunch of stuff then it kind of clutters
the page and we just click on that
right?
It doesn't really need to be there anymore after I've checked it off.
I want to clear out my list.
So I want to be able to delete the item when I click the check box next to it. And to do that we have
to remove that particular document from items collection. Inside our list.
ejs
you can see that
currently we have this input called checkbox and next to it we have the item name in a paragraph.
If we wanted to send some data when that checkbox gets clicked then we need again a form and a post
route.
So let's go ahead and create a new form and we're going to wrap that form around our items div. Once
we've grouped our input and a paragraph into this new form
the first thing that you notice when you hit save and you go back over to our todo list is when you
refresh the styling looks a bit different
right?
It looks a bit off.
And the reason for this is because in our stylesheet,
so if you go into public, css, styles.css, you can see that we have the CSS rule which is targeting
our HTML form elements and this will apply the text-align center and margin-left to all of our form elements
which is not what we want.
We only want it for this form and not this one.
So what's the difference here?
Well this one has a class of item.
So it's got a class and it's also a form element.
So if we wanted our CSS to be more specific then we could say for this rule it should only apply
to all of the HTML forms which also have the class item.
Now if we hit save and we go back to our To-Do list and hit refresh then you can see that our previous
styles are restored.
So now that we fixed the CSS and styling, the next thing to address is where is this form going to post
to? Well in order to specify that we need an action and a method right?
So if we just go ahead and copy those into this form element then the part that we're going to keep
the same is this form is also going to make a post request but the route is now going to be separate
from what we had down here.
This route was responsible for adding new items,
so this route has to be responsible for deleting checked items.
Let's add a route called /delete and hit save.
Now inside our app.js just below our other posts route we can add this new route. So we can say
app.post and the route was called /delete and this also is going to have a callback, req and
res and now I'm going to console log simply request.body
so what is being sent over basically from our form inside list.ejs because we know that over here in
our app.post we're getting the new item being sent over because in that form we have an input with
type text and has the name of new item and we're going to send over whatever it is that the user typed
in here over to this route via the req.body.
If we run our app now let's see what our console logs for the req.body of our /delete
route.
I'm going to refresh my to-do list and I'm just going to check one of these boxes.
And if we have a look inside our console then you can see that absolutely nothing happens here.
And the reason is because inside our list.ejs, if you remember we actually have this button
here and it's type is submit.
And whenever we have a submit button anywhere inside a form then its job is that when it gets pressed
it will submit all of the data inside the form and make the post request to the required route.
Now in this form however we don't have any way of submitting and it'll look kind of ugly if we added a submit
button next to the checkbox right?
I mean, ideally what we want is whenever we tick a checkbox we just want it to be able to delete that
item.
The action has to happen when we check the checkbox.
So how can we get the form to submit when the checkbox is checked?
Well if we didn't have any better ideas then we can always ask a developer's best friend
right?
It's Google.
So if we put in the query "submit form when checkbox is checked" for example.
That seems pretty much like what we want.
And if we click on the first Stack Overflow answer, then the person is asking how do you submit a form
when a checkbox is checked.
Sounds like what we want too.
So if you scroll down, the first answer tells us that in plain Javascript simply add this attribute on
change to the input that is going to change and then inside it
just add a little snippet of Javascript code to submit the current form.
Let's go ahead and copy this over to our input.
The input that we want to trigger the submit is of course our checkbox.
In addition to having a type it's now also going to have a onChange attribute. And so that means when
the checkbox is changed,
so from checked to unchecked, unchecked to checked, then we're going to run this line of Javascript code
which will take the current form that the input is inside,
so this one, and it's going to submit it to make that post request to our delete route.
In addition to just getting the form to submit we also need a way of accessing this input. To do that
we have to give it a name. For our text input to add new items
we gave it a name of newItem which is what allowed us to tap into it inside the req.body.
So for this check box we're also going to give it a name and I'm going to give it something really simple.
I'm just going to call it checkbox.
So now if we save our code and go back to our console and refresh our page then I'm going to click on
one of these boxes to check it.
And now if I have a look inside my console you can see that I've got the "on" value printed for my check
box. And this comes from the log statement that's inside my app.js inside this route.
This is what is being logged right now.
And of course I can now tap into this thing called checkbox.
So if I hit save now and I refresh my page and go back to my root route, refresh my page and check one
of the items, you can see
I now get the value of that req.body which is "on". On is not very useful for us
right?
What we want is the item name that they actually checked off.
So we know that our forEach loop is going to render as many checkboxes as we have items in our items
collection.
That means that we can also assign a value to our checkbox based on the item's ID. Inside here I'm going
to add another attribute called value and this value is going to be equal to some EJS values.
So I'm going to add that classic angle bracket percentage equal sign and then I'm going to close it
off with percent angle bracket. And inside here
I'm going to tap into that item object that we get access to and I'm going to tap into its ID.
Remember, when we pass new list items over we're passing over an array of Mongo documents.
Each of these documents or each of these items that we're looping through has a name as well as an ID.
So if I bind that ID to the value of each of the checkboxes then when it gets checked I can submit this
form and find out which item was actually checked off.
So now let's hit save and let's again refresh our home page and check off one of the items
and now you can see I've got the ID of that item.
So once I've got the ID then everything else is so much easier because we can now delete the item with
that particular ID.
Inside our app.js instead of console logging this, I'm going to bind it to a constant and it's
going to be called the checkedItemId and it's just going to equal req.body.checkbox.
And now as a challenge I want you to remove the item from the database that has the ID of the one that
was checked off. Again as a reminder, in the Mongoose documentation
you can find a method called findByIdAndRemove
which seems pretty much like what we need to do.
So in this case we simply provide the ID as an argument and then we have a callback to handle any errors
or log success.
And when this is executed, it should delete that document with that particular ID.
Pause the video and try to complete the challenge.
All right. So let's do that right here where we've got access to that ID.
I'm going to tap into the items collection using that item model and then I'm going to call
findByIdAndRemove. And make sure that you don't have any typos in there.
And then the ID is of course going to be the checkedItemId and then we're going to provide that callback
to see if there were any errors. And if there were no errors then in that case we're simply going to
log that we "Successfully deleted checked item".
Now if we check the Mongoose API documentation and you locate the findByIdAndRemove method, if you
read through it ill tell you that if you don't provide the callback it won't actually execute the delete.
It's only when the callback is there does it actually execute it.
Whereas if it only has an ID or option, then it'll just return the query.
It'll just find that item by ID.
So in order to get the and remove part,we have to provide the callback
even if you don't care to check if there were any errors or not. Let's hit save and let's go back to
our root route and try to delete one of these items.
Again our web interface is not updating but if you go over to the console it says that it successfully
deleted the checked item which means that it's probably been deleted
and if we again check our Mongo shell and we do "db.items.find" then indeed that item that
we just checked off here hit this to delete an item no longer exists in our items collection.
But in order for it to reflect in our web page then again we have to call a redirect right? We can say
res.redirect to the home route.
And now it will find all of the items that we still have in our items collection and render it on the
page.
So now let's go back to our home page and let's delete some of these random ones
then you can see that they're all just getting crossed off and deleted.
Pretty neat right?