WEBVTT 0 00:00.480 --> 00:04.410 So now the functionality for our ToDo list 1 00:04.410 --> 00:07.500 version 2 is almost complete. 2 00:07.500 --> 00:13.770 We're able to access our default list on the home page and we can add new items here 3 00:14.070 --> 00:16.230 which gets added to our database. 4 00:16.230 --> 00:22.450 We can delete them from our database and we can also create custom lists 5 00:22.470 --> 00:24.620 for example one called shopping 6 00:25.050 --> 00:25.310 right? 7 00:25.310 --> 00:31.890 So we now have a new list with three default items and we can write something like "Buy milk" and it will get 8 00:31.950 --> 00:36.540 added to that particular list and all of this gets persisted. 9 00:36.540 --> 00:43.920 So whatever it is that we decide to save into the home list or into the worklist then they always stay 10 00:43.920 --> 00:47.840 where they're meant to be associated with that particular list. 11 00:48.330 --> 00:55.050 So the last thing that we need to do is we need to be able to delete these items in our custom lists 12 00:55.590 --> 01:04.950 because at the moment when I check this off then it somehow redirects me back to my home page instead 13 01:05.010 --> 01:09.910 of actually deleting the item that I wanted from that custom list. 14 01:09.930 --> 01:11.160 You can see it's still there right? 15 01:11.170 --> 01:14.440 So why is this? 16 01:14.710 --> 01:22.480 Well, if you take a look inside our list.ejs at the moment the way that we're implementing the delete 17 01:22.480 --> 01:30.850 functionality is through the use of this form. And this form makes a post request to the /delete 18 01:30.850 --> 01:40.750 route and it passes over the item id that the user wanted to delete and the form gets triggered when 19 01:40.780 --> 01:43.390 one of the checkboxes gets clicked. 20 01:43.390 --> 01:47.360 So here's a problem. In that route 21 01:47.360 --> 01:55.790 we only look for all of the items but we don't currently check which list the item is from in order 22 01:55.790 --> 01:59.130 to delete it from the correct list. 23 01:59.150 --> 02:05.840 So the first thing we need is that when we tap into this route we need two pieces of information: the 24 02:05.870 --> 02:13.880 ID of the checked item but we also need to know which list that item came from. In this form 25 02:13.880 --> 02:20.180 there isn't a submit button right? Where we can put in our list title and pass that over when the form 26 02:20.180 --> 02:21.650 gets submitted. 27 02:21.650 --> 02:27.770 There's no other input other than our checkbox which already has a value and we definitely need that 28 02:27.770 --> 02:30.300 value of the item ID. 29 02:30.710 --> 02:31.910 What can we do here? 30 02:32.830 --> 02:39.950 Rather handily in the list of inputs that we get from HTML, there's one which has a type of 31 02:40.000 --> 02:41.150 hidden. 32 02:41.260 --> 02:48.250 And this allows us to include data that can't be seen or modified by users when a form is submitted. 33 02:48.280 --> 02:57.240 For example the id of the content or in our case the list name. So inside here just before the form 34 02:57.270 --> 02:57.870 ends, 35 02:57.870 --> 03:08.710 I'm going to add a new input and this input is going to have a type of hidden. And it's also going to 36 03:08.710 --> 03:15.340 have a name so that I can refer to it when I look at my req.body and the name for this is going 37 03:15.340 --> 03:18.300 to be called just listName. 38 03:18.490 --> 03:20.720 And finally it's going to have a value 39 03:20.800 --> 03:23.500 and this is where the neat part comes in. 40 03:23.500 --> 03:26.750 We're going to use that EJS tag again 41 03:27.070 --> 03:34.860 and inside here we're going to insert that listTitle that we already have access to. 42 03:35.160 --> 03:42.930 And now we can close off our inputs and make sure that we add a closing input tag because it's not 43 03:42.960 --> 03:44.820 a self closing tag, 44 03:44.970 --> 03:47.840 these inputs. 45 03:47.860 --> 03:56.260 So now if we hit save and we head back to our app.js in our post to our delete route we can now 46 03:56.290 --> 04:04.030 check to see what is the value of that listName. So we can create a new constant and we can call it 47 04:04.030 --> 04:05.420 list name. 48 04:06.340 --> 04:15.460 And we're going to set it to equal req.body.listName. And just check to make sure that is 49 04:15.460 --> 04:18.070 the same as it got here. 50 04:18.400 --> 04:20.260 So yep! Looks good. 51 04:20.260 --> 04:27.010 So the next thing we need to do is we're going to again use an IF statement to check to see if we are 52 04:27.310 --> 04:35.710 making a post request to delete an item from the default list where the listName is today or if we're 53 04:35.710 --> 04:44.680 trying to delete an item from a custom list. We're saying if listName is equal to today then that means 54 04:44.770 --> 04:49.750 we're on the default list and we can do everything as we did previously. 55 04:49.750 --> 04:56.150 So we'll just cut and paste that into that if block, but what about the else block? 56 04:56.290 --> 05:00.120 Well this is the case where the list name is not today 57 05:00.460 --> 05:06.520 so that delete request is actually coming from a custom list. 58 05:06.560 --> 05:14.060 In this case we need to be able to find the list document that has the current listName and then we 59 05:14.060 --> 05:20.820 need to update that list to remove the checked item with that particular ID. 60 05:21.050 --> 05:28.700 Now because inside our list document there's an array of item documents 61 05:28.700 --> 05:36.290 then it's actually a little bit more complex because we basically have to find an item inside this array. 62 05:36.320 --> 05:42.860 We have to basically trawl through this array and find an item with a particular ID and then remove 63 05:42.890 --> 05:46.750 the entire item from the array. 64 05:46.760 --> 05:48.310 There's a few ways of doing this. 65 05:48.350 --> 05:53.500 You can of course use FOR loops and use the Javascript filter method, 66 05:53.660 --> 05:56.590 but let's see if there's actually a simpler way. 67 05:58.530 --> 06:08.430 If we head over to Google and we simply just search for "mongoose remove document from array". That seems 68 06:08.880 --> 06:11.680 pretty much like what we've been trying to do 69 06:11.700 --> 06:12.360 right? 70 06:12.690 --> 06:17.420 Let's see what the first non-ad Stack Overflow post says. 71 06:17.910 --> 06:24.750 So in this case they're saying you want to delete an array element in a document and then save. 72 06:24.750 --> 06:29.140 So that sounds kind of similar to what we wanted to do. 73 06:29.460 --> 06:37.670 If we look at the answer, the first answer says that you can do that directly in MongoDB. And you 74 06:37.670 --> 06:45.260 can use one of the pull or pullAll operators to remove an item from the array. 75 06:45.260 --> 06:47.950 Now we have a bit of a hint to go on. 76 06:47.990 --> 06:52.190 So we need to use this thing pull or pullAll. 77 06:52.360 --> 06:59.530 So let's search the MongoDB docs and we're going to look for this pull operator to find out a bit 78 06:59.530 --> 07:00.970 more information about it. 79 07:02.420 --> 07:11.210 So it tells you that the pull operator removes from an existing array all instances of a value or values 80 07:11.480 --> 07:18.480 that match a specified condition. And they've shown you an example of how you can use this. 81 07:18.620 --> 07:28.520 So we can use this along with the Mongoose method which is called findOneAndUpdate to combine these 82 07:28.520 --> 07:35.000 two together in order to achieve what it is that we need because with Mongoose alone or with Javascript 83 07:35.010 --> 07:38.870 alone we can actually get this really neat pull functionality. 84 07:38.960 --> 07:45.260 We have to use something that's fundamental to MongoDB. And all the operators that's preceded by a 85 07:45.260 --> 07:51.800 dollar sign comes from MongoDB. And you can see if you scroll down there's actually a whole bunch of 86 07:51.890 --> 07:57.860 other operators you can use as well but this one is far more efficient than if we tried to loop through 87 07:57.860 --> 08:03.440 all of the items in our array, reading each of them and then removing them through the filter method that 88 08:03.440 --> 08:04.830 Javascript gives us. 89 08:04.940 --> 08:10.460 This is just a slightly more efficient way of getting to the end result which we found through just 90 08:10.460 --> 08:13.120 doing a quick Google search. 91 08:13.170 --> 08:14.610 This is what the mongoose 92 08:14.610 --> 08:15.520 findOne 93 08:15.530 --> 08:23.100 AndUpdate syntax looks like. We first specify the model which corresponds to the collection that we want 94 08:23.100 --> 08:25.710 to find one and update from, 95 08:25.890 --> 08:27.960 and then we call findOneAndUpdate 96 08:28.230 --> 08:30.960 and finally we provide three things: 97 08:30.990 --> 08:33.720 one is the filter conditions, 98 08:33.720 --> 08:35.500 what do we want to find right? 99 08:35.520 --> 08:37.030 This is the query. 100 08:37.290 --> 08:40.660 And then the second one is well, what do you want to update? 101 08:40.770 --> 08:47.610 And the final thing is a callback where we get the results of what we found based on those conditions. 102 08:49.150 --> 08:54.340 So in our case the conditions we've already seen before and the callback we've really seen before. 103 08:54.460 --> 08:58.670 But in this part where we have to provide the updates, how do we actually do it? 104 08:58.810 --> 09:00.650 Well let's break it down a little bit more. 105 09:00.910 --> 09:02.950 We want to use the pull operator. 106 09:03.070 --> 09:11.470 So inside there, we have to specify the $pull operator as a key and then the value has to be the field 107 09:11.470 --> 09:13.020 that we want to pull from, 108 09:13.210 --> 09:15.810 so this has to be an array of something. 109 09:15.910 --> 09:23.050 In our case is an array of items. And then we have to provide a value for that field, which item in tha 110 09:23.110 --> 09:24.170 array of items. 111 09:24.250 --> 09:26.240 do we actually want to pull? 112 09:26.380 --> 09:30.050 So the final syntax looks something like this. 113 09:30.430 --> 09:33.670 And it's a little bit complex which is why I broke it down. 114 09:33.670 --> 09:37.980 But essentially we're saying we want to pull from a particular array 115 09:38.230 --> 09:45.970 and the way that we're going to find the item inside that array is through its ID or through its name 116 09:46.060 --> 09:48.170 or whatever it is you want to choose. 117 09:48.370 --> 09:50.160 And then we have to provide the value. 118 09:50.200 --> 09:53.790 So let's understand this a little bit better by seeing it in action. 119 09:55.330 --> 10:02.140 Inside our delete route and inside this ELSE block where we've checked that the list that the delete request 120 10:02.140 --> 10:04.530 came from is not our default list, 121 10:04.570 --> 10:13.090 we're going to tap into our list model and we're going to call findOneAndUpdate and just make sure 122 10:13.090 --> 10:14.590 you don't have any typos in there. 123 10:16.040 --> 10:18.890 And then we have to specify three things right? 124 10:18.890 --> 10:20.970 The first is the condition, 125 10:21.020 --> 10:24.140 so which list do you want to find? 126 10:24.470 --> 10:26.390 And we're only going to get one back, 127 10:26.390 --> 10:29.440 remember. This is how findOne works. 128 10:29.450 --> 10:32.750 The second thing is what updates do we want to make? 129 10:33.050 --> 10:36.200 And the last thing is simply just a callback. 130 10:36.200 --> 10:40.210 Now in the first part this is quite easy. 131 10:40.280 --> 10:48.200 The list that we want to find has to have a name that corresponds to this listName constant that we 132 10:48.200 --> 10:49.480 got from our list. 133 10:49.500 --> 10:51.830 ejs. Let's put that in there. 134 10:51.830 --> 10:58.220 This is going to be the name of our custom list. Now that we found that document that corresponds to 135 10:58.220 --> 10:59.540 the customer list, 136 10:59.570 --> 11:02.020 how are we going to update it? 137 11:02.540 --> 11:10.190 Well first we need to use the pull operator, and then we're going to specify something that we want to 138 11:10.190 --> 11:18.890 pull from and here we have to provide the name of the array inside this list that we found and that 139 11:18.890 --> 11:22.140 name is of course going to be items. 140 11:22.160 --> 11:27.410 This is the only thing that's an array inside our list document. 141 11:27.420 --> 11:31.970 So now we can say pull from the items array. 142 11:32.010 --> 11:36.950 Now how do we know which item out of all of the items we want to pull? 143 11:37.140 --> 11:44.540 Well here's another further set of curly braces where we provide the query for matching the item. 144 11:44.640 --> 11:51.600 The query we're going to make is we're going to pull the item which has an ID that corresponds to the 145 11:51.630 --> 11:53.830 checkedItemId. 146 11:54.300 --> 11:56.850 So we're going to put that in there. 147 11:57.390 --> 12:05.340 And now this three layered cake is finally complete and this is the entirety of our update statement 148 12:05.830 --> 12:13.540 where we pull from our items array an item that has an ID that corresponds to our checkedItemId. 149 12:13.590 --> 12:15.940 And finally we're ready to write our callback. 150 12:15.990 --> 12:23.550 So again it's going to be a callback that gives you an error and a found list because in this case when we're 151 12:23.550 --> 12:29.910 calling findOneAndUpdate the find one corresponds to finding a list. 152 12:29.910 --> 12:39.680 Now that we've found the list and we've updated it then if there are no errors we can simply res. 153 12:39.910 --> 12:48.130 redirect. And we're going to redirect again to that custom list path 154 12:48.150 --> 12:54.600 so it's going to be "/" + listName. 155 12:54.810 --> 12:59.090 We're finally ready to save and test our app. 156 12:59.220 --> 13:06.840 So if we're on the work route, let's just refresh that page, and I decided to delete this item. 157 13:06.840 --> 13:13.550 Hit this to delete an item, then I click on it it gets deleted I get redirected back to the same list. 158 13:13.710 --> 13:15.960 Let's delete another one just to be sure. 159 13:16.020 --> 13:18.150 Again I get redirected to the same list. 160 13:18.180 --> 13:24.320 So now we're able to create custom lists and we get to add items 161 13:26.900 --> 13:35.720 and we get to delete items from our custom list as well as adding items to our default list and deleting 162 13:35.720 --> 13:36.940 them there as well. 163 13:38.560 --> 13:46.120 Now there's just one last niggling thing which is when we create our route parameters as we saw previously 164 13:46.420 --> 13:52.120 it actually differentiates between a lower case work and a upper case work. 165 13:52.120 --> 13:59.170 So in this case I'm trying to access localhost:3000/home with a lowercase "h" and 166 13:59.200 --> 14:04.850 you can see here I get this list that has a title of lowercase home. 167 14:04.900 --> 14:11.530 So let's go ahead and add a new item, "Buy sofa" to our home list. 168 14:11.530 --> 14:18.990 Now if I switch over to /Home, then it's a completely different list right? 169 14:19.000 --> 14:23.620 And that's crazy because the user probably wanted the same list. 170 14:23.650 --> 14:33.470 How can we solve this? What if we always wanted the capitalized version of the list in our title? 171 14:33.530 --> 14:41.180 How can we change whatever inputs that the user makes into that custom listName and change that string 172 14:41.270 --> 14:42.880 into a title case? 173 14:43.040 --> 14:46.100 So always capitalize the first letter. 174 14:46.670 --> 14:49.150 There's again lots of ways of doing this 175 14:49.340 --> 14:53.290 but the easiest way is just to simply use trusty Lodash. 176 14:53.570 --> 15:00.500 So let's go ahead and exit out of our server and let's npm install Lodash. 177 15:00.530 --> 15:05.980 Now while that's doing its thing, let me just show you the documentation for Lodash. 178 15:06.110 --> 15:14.770 If you scroll all the way down to the strings section you can see that we tried out kebab case, lower 179 15:14.770 --> 15:16.840 case, lower case first, 180 15:17.140 --> 15:23.890 but there's also this one called capitalize. And if you read through the docs you'll see that this one 181 15:23.980 --> 15:30.240 converts the first character of a string to uppercase and the remaining to lowercase. 182 15:30.250 --> 15:37.420 So that means even if you write all caps FRED or all lowercase fred, whatever it may be, it'll always just 183 15:37.420 --> 15:44.770 capitalize that string and only change the first letter to uppercase and all the rest to lowercase. We 184 15:44.770 --> 15:52.330 can add Lodash into our app with the Lodash convention which is a underscore and we're going to let it 185 15:52.390 --> 15:57.170 equal require "lodash". 186 15:57.250 --> 16:04.940 So now whenever we need some functionality we can just tap into it using that underscore. And the place 187 16:04.940 --> 16:09.030 where we need it is inside this route. 188 16:09.050 --> 16:14.390 So instead of setting the customListName to just simply whatever it is that the user typed in here, 189 16:14.780 --> 16:22.340 we're going to modify it to make it capitalized. And we can call that method by simply saying _ 190 16:22.520 --> 16:29.800 .capitalize and then inside the parentheses we'll provide a string just as you can see in this example. 191 16:30.720 --> 16:37.520 Here we're going to say customListName is equal to _.capitalize 192 16:37.680 --> 16:44.820 and remember because we're working with stuff created by people in America we have to respect the spelling. 193 16:44.820 --> 16:49.570 So it's a capitalize with a "z" instead of a "s". 194 16:49.840 --> 16:58.010 So now if we hit save and we go back to our console and we restart our server with nodemon. 195 16:58.660 --> 17:05.290 Now before we test our app we first have to drop our current collection because if you check out db 196 17:05.290 --> 17:13.450 .lists.find you can see we currently have the uppercase home, the lowercase home and it's all 197 17:13.660 --> 17:14.220 a mess. 198 17:14.260 --> 17:18.650 And this is before we enabled that functionality using Lodash. 199 17:18.880 --> 17:25.460 So let's go ahead and do db.lists.drop and hit enter. 200 17:25.480 --> 17:26.920 Now that's been executed. 201 17:26.950 --> 17:31.050 So if we do db.list.find you can see it's completely empty. 202 17:31.050 --> 17:35.790 Now let's go ahead and try to navigate to localhost:3000 203 17:35.800 --> 17:39.830 /work with the lowercase "w". 204 17:40.120 --> 17:45.760 And remember that Chrome loves to auto suggest. So sometimes you might think you're going to the capital 205 17:45.760 --> 17:50.140 one but it actually automatically redirected you to the lowercase one. 206 17:50.140 --> 17:53.780 So just to be sure just click on the one that you see in here. 207 17:53.890 --> 18:04.000 So here I'm going to /work and now I'm going to add a new item "Write emails" and 208 18:04.030 --> 18:10.790 hit add. And then I'm going to change this to the capital version of work, 209 18:10.840 --> 18:11.670 hit enter. 210 18:11.770 --> 18:18.180 You can see I'm still on the same list. And it's the same thing if we change that to lowercase 211 18:18.220 --> 18:24.850 home, it'll always redirect me to the list with a capitalized name. 212 18:25.030 --> 18:30.780 Even if I decide to do something really crazy like "HOMe". 213 18:30.900 --> 18:38.920 So something that just completely messes up the casing then I still get taken to the right page. 214 18:38.930 --> 18:45.650 I hope you enjoyed building this along with me and learning how to add a database to our Node.js apps 215 18:45.920 --> 18:54.650 that will make your apps infinitely more powerful and be able to permanently store your user data. In 216 18:54.650 --> 18:55.750 the next lesson 217 18:55.760 --> 19:03.200 it's your turn to add a database to the blog website that you created earlier on and it's finally time 218 19:03.230 --> 19:06.200 to actually turn it into a real product. 219 19:06.200 --> 19:09.080 So for all of that and more, I'll see you on the next lesson.