Currently as it is our list works in terms of all of the basic functionality. We're able to add new items,
we're able to delete items and we're able to look at all of the items in our list.
But there's one feature or one functionality that I would love to have for this app that we haven't
currently implemented and it's the ability to have multiple lists
right?
There is no fun just having a single is to work off.
So what if we were able to instead of going to the home page which is our default list what if we were
able to just write /work and head over to the worklist and be able to see the items there.
And what if we could create new lists on the fly like I wanted to create a home list,
so if I type in the web address /home then I wanted to create a brand new page without me having
to define all of the get and the post routes. We can use what we learn about express route parameters
to do this.
Express allows us to use route parameters to create dynamic routes. And so as a challenge I want you
to delete this get route and instead create a dynamic route based on the route parameters so that if
I go to localhost:3000/Home or /Work I should be up to print
this last part of the URL inside the app.get. Again as a reminder this is what Express route parameters
look like.
We have a colon
and then we have a parameter name and then inside the callback we get Access req.params. that
parameter name.
And this will equal the value that the user has typed in here.
Pause the video and try to complete this challenge.
So let's begin by creating our route.
Maybe we'll create it just after the app.get up here.
So I'm going to write app.get and instead providing a hardcoded route like /Work or
/Home,
I'm just going to write /: and then I'm going to call it customListName.
And this is going to again accept a callback request and response.
And then inside my callback I'm going to log req.params.customListName.
So now let's hit save and go into our browser and I'm going to try and run this again.
Localhos:3000
/Home
and now if I go into my console you can see I now have that part at the end here logged in my console.
So I could change that to say work and if I go ahead and hit enter, then you can see I now get work logged
in here.
So far so good.
Now instead of logging that param let's go ahead and save it into a constant and let's call that custom
ListName. So customListName equals req.params.customListName and that comes from whatever
it is that the user enters after the forward slash.Now that we have access to the name of the new list
well we get to use it to create a new document and that document is going to have a slightly different
schema than what items had.
So right below where we have our default items I'm going to create a new schema and this one is going
to be called listSchema. And this is going to take two fields:
one is the name of the list which will be a string
and the second is going to be an array of items.
So the name of the field is going to be items and the value of the field is going to be an array of
itemSchema based items.
So now what we're saying is for every new list that we create the list is going to have a name and it's
going to have an array of item documents associated with it as well.
The next thing to do as always is to create our Mongoose model.
This is going to be a constant called list and it's going to be equal to mongoose.model and then
we're going to specify the singular version of our collection which is going to be called a list.
And we're going to specify the schema which is listSchema.
And make sure that you haven't got any typos like I have here
"mongoose.mode" which should be "mongoose.model".
So now that we've created our list model from our listSchema, then we're ready to create new list documents
based off this model. And we're going to do that when a user tries to access a customListName. Inside
this route
I'm going to create a new list and this is going to be created based off our list model.
And I have to fill in the two fields:
one is the name of the list which is simply going to be whatever the user typed into the route.
So if they said /Work or /Home then that's going to be the list name,
so I'm going to put a customListName here.
And then the second field is going to be the items.
And this should accept an array of list items.
And so when we create a new list that is a custom list we're going to give it the name based off the
path that the user tried to access
and then for the items we're simply going to start off with the same default items as we created previously.
So previously when we made our default list we created 3 item documents and we packaged them all into
an array.
So we can now reuse this array to populate our custom lists.
So inside here the items for my new list are simply going to be the default items.
Now that we've created our new list document then all we have to do is call list.save to save that into
the lists collection.
Now if we save our app.js and we head over to our browser and we try to access for example
localhost:3000/Home and we hit enter, then if we go into our Mongo shell and we
just say "show collections" then you can see we now have a new collection and that collection is a collection
of lists.
So let's say "db.lists.find" and you can see that we have a single document inside our lists collection
which has the name of home which corresponds to this part that we entered using the route parameters
and then it has an array of items which correspond to our default items.
"Welcome to your todolist!
Hit the + button and then hit this to delete the item".
So so far so good.
That was pretty successful.
Now here's the problem.
When a user accesses local host or whatever the website domain will be /Home
then we'll create a new list with that name home
right?
But we don't really want a whole bunch of them.
What if it users already created the list and they simply want to access it?
So for example if I head back over here and we stop this and try to head over to Home again then if
we check our Mongo shell and we try to run "db.list.find" then you can see we now actually
have two lists both called home
right?
So this is not what we want.
We don't want to create a new list with the same name every time the user tries to access it. How can
we solve this with code?
Well, we have to check to see if a list with that name already exists in our collection of lists. And
if it does then we're just going to display it
but if it doesn't then we'll create a new one using the default items.
So as a challenge, I want you to use the Mongoose method called findOne to find within our lists collection
whether if there's a list that has the same name as the one that the user is currently trying to access.
And if there is, then we're just going to log "exists" and if it doesn't exist then we'll log "doesn't exist".
So pause the video and try to complete that challenge.
All right.
So just above where we create our list we're going to call findOne on our list model. And the condition
we're going to give it is
find me a list inside the collection of lists that has the name
that's the same as the one that the user is currently trying to access,
so custom list name. And once you've completed that then we'll have the callback function with error
and foundList.
So that means if there was an error then it'll print it,
but if there wasn't then we should be have to tap into what was found.
So we can say if there were no errors, then in that case we're going to check to see if there is or there
isn't a foundList.
Now it's important to differentiate between findOne and the one that we used previously which is find
all essentially because this method is going to give us an array back as the result.
And that's why we have to check to see if the array had a length of 0
namely it had no items in it.
It was an empty array.
But in this case what we get is an object back because we're saying findOne
so you'd only return one document if it is found.
So we can't check it's length.
But instead we can simply check to see if it exists or not
right?
If foundList doesn't exist
well then in that case we'll log "Doesn't exist".
But otherwise then it will log "Exists." Now let's give that a go
and let's go into
our website and we try to access localhost:3000/Home
Let's hit enter and see what we get in our console.
So let's switch back to this tab and it says "Exists".
But what if we tried some sort of new one that we've obviously not made yet and
hit enter and "Doesn't exist".
Great!
So our code seems to be working.
So now instead of console logging we're going to go ahead and implement the code that we need. Here
I'm going to write a comment saying this should be the path where we create a new list.
And down here instead of this console log I'm going to have a comment that says this is a path where we should
show an existing list.
So if there were no list that were found that have the same name then we're going to create a new list.
And the part of our code which does that is of course this section.
So we'll just cut that and paste it into the IF and then we have the ELSE statement.
So show an existing list if it already exists here.
We're going to use that found list that we tapped into
and we've really checked that it's definitely not nil,
it definitely exists.
So what we can do is we can call res.render and we're going to render our list.ejs
and then the second parameter we're going to pass in is going to be the list title and also the items,
so let's just copy that in here.
Now the list title is no longer going to be a static word like today.
Instead it's going to be the customListName right?
So we could tap into this found list and we can say .name to tap into its name field.
And for the items we do something quite similar. We say foundList.items
and so now we'll tap into the name property and the items property and we'll send that over to list
.ejs to populate that page.
So now let's hit save and let's head back into our localhost:3000 which still works as it should.
And this one of course has the default list with the static name of today.
But let's go ahead and access our home list.
So we've now got a home list and what if we try to create a new one, say a worklist.
Well it seems like the list got created because if we check in our Mongo shell then you can see there's
a whole bunch of stuff that got created but the last document that we added was this one with a name
of work.
If you wanted to clear out a particular collection because it's kind of gathering dust and it's got
data from previous testing, then all you have to do is to make sure that you're inside the correct database,
so I am, I'm using todolistDB.
And then here we can show collections.
So we've got items and lists and I can say "db.lists.drop" and this will wipe out my lists collection
so that if I say "show collections" again, lists no longer exist. Let's head back to our web page and access
home again.
So that should now be created inside here and "show collections" should now list list and db.lists.
find should now list a single document with name of home.
And if I change that to work and hit enter then
now I should have two documents: one called home and one called work.
But why are they not automatically showing up in here?
Well when we create a new list we fall into this IF block and all we do at the end of it is we just
call list.save and then we're done with it. We're leaving the user hanging right?
So we're going to use our neat trick of using res.redirect and we're going to redirect back to
the current route which remember, is not just the route route but it's /customListName.
But luckily we already have access to that inside this IF statement so we can say redirect to /
and then we're going to concatenate the customListName.
Now if we head back into our website and we tried to create a new list say "School" and hit enter,
you can see we are redirected to that route and we've now got our list showing up here.