Transcripts
1. Welcome to the course!: Welcome to Full Stack Web Development for beginners. This course is designed to take you from a beginner to a web developer capable of building not only the front-end user interface, but also to design and build the backend to interact with too. Who am I? My name is Chris and I will be your instructor throughout this course. I am a web developer and also the producer of lots of tutorials, teaching thousands of students the skills they need to build websites and applications. This is the fourth part in the full series, where we will continue to build on everything from the first three parts of this course. This course is all about moving to the backend of websites, where you will learn lots of amazing things. We start off with node and the express framework, where we setup a travel book in application called Let's Travel. We will build this project throughout the course. Everything you learn will be immediately put into practice. Node and Express are really popular and learning these technologies will leave you in high-demand. After this, we move on to routing and templating, which will allow us to switch between pages and navigate through our app, along with setting up templates to display not just the content we've created, we'll too also render data from our database too. Databases is what the next section is all about, where we'll look at MongoDB and Mongoose. You will learn so much including modeling our data, create, read, update and delete actions, along with filtering and aggregation to get back the exact data which we need. After this, we push on with styling and adding lots of nice features to our project, along with learning many next-generation JavaScript techniques from ES6 and beyond. You will also learn how to allow users to upload images and how we can save and retrieved these images from our Cloud storage. Of course, most apps nowadays need to deal with user accounts and authentication. This is something which we'll learn about while also applying all of these to our project. You will learn how to create user account, login in and out, validating new user's information, authentication, storing passwords safely and so much more. We finish off our app by allowing the user to place orders and then save them to the database. Then we create an account area where the user can log in and see any bookings which they have made. Also admin users can go into the admin section to see lists of all orders which have been placed. This course ends by showing you some security tips and recommendations to prepare your application for production. We then push our app to a live server for the rest of the world to see, all while explaining things in a straightforward, simple to understand way, which even beginners will understand, along with reinforcing things with a fun yet challenging projects as we go. I hope you're really excited to learn about all of these things, in the final part of this course.
2. What we will be building: In this section and for pretty much the remainder of this course, we're going to build the niche travel application, when user can login and make bookings for hotels in various countries. This is going to be the homepage, which is going to be the starting point where a user can search for hotels in different countries. We can also add the number of nights and also the number of guests, as well as certain filters such as the star rating, and we can also solve the hotel's price wise from low to high or high to low. Further down, we're also pulling in some random hotels from the database and restricted needs to be the first nine. The user can click on any of these hotels and find out more information. He will get an extended view of the hotel. We also get a description and then at the bottom, we get the chance to then search for this hotel with the current availability and also the current price. If we go back to the homepage further down at the bottom after the hotels, we also have a list of countries where we can filter down the hotels by the various countries. If we click on each one of these, we can then see each hotel which is available for that particular country. Along with these filters, we can also click on the "All Countries" and then see a list of all available countries which we have hotels available for. Once we add new hotels as an Admin, these countries are automatically updated on the front-end too. So we don't need to worry about adding all these in, each time we add a new country, as well as these countries. If we go back up to the hotels, we also have a link to see all the available hotels. If want to scroll through all the ones which you have on the database rather than filtering down by the countries by using a Search. Service application will be a node-based application using the express framework which we'll learn about in this section and the forthcoming sections two. Will also be using various technologies to join this project, such as MongoDB to store our data. We use Mongoose for various things such as structuring our models or other data. Along with all this, will also have the facility for the user to go ahead and login. We can log in with an email and password. So let's listen now. Will also be using flush messages, just like you see here, to give the user some feedback about when a login or logout, along with the admin to be able to see when a hotel has been successfully added to the database. Once we login, we now have an account section which we can click on. We can also check out the current bookings which each user has available. Along with this, if the user is an admin, so let's go to '"/admin". Currently, the username and password which I've entered is down as an admin. So we now have access to this admin section of the website, which is going to be the back end where can have some administrator privileges, such as adding new hotels to the database, which we can do with this form here. The admin can also edit details of a hotel or completely remove them from the database too. All we need to do is, add the hotel's ID, or we can search by the hotel name, if we're not sure of the ID. Back to the admin, we can also view all of the available bookings which being placed by all users. Once we're logged in as a user and perform search, let's search for Jamaica, seven nights, and we can add a date, number of guests, the star rating, and also the price filters. We can click on "Search" and then we've taken it to the search results. So this is the hotel which matches our filters and we'll also have a section on the right-hand side which calculates the total per person and as well as the total cost of the booking. We can then continue through to the confirmation screen which provides the full description of the hotel along with the facility to place an order. Once it is placed on and taken to our counts and then we can see down at the bottom that our order has been added down at the bottom with a unique order reference. If you go to the admin, it should also be added inside here too because we're logged in as an admin too for this current user. We can also go to View bookings, and then see the bookings placed down at the bottom too. If we're to go ahead and log out and then try this admin section. We can see we're redirected back to the homepage because this route is protected. So during this project, we're going to be learning a variety of things. As I mentioned before, we'll be using node for the back end, express as the framework to build this project, Mongoose and MongoDB for our back-end data, route into navigate through our projects along with the MVC pattern which will allow us to add some structure to our project too. Along with this will learn so much more including authentication, how to keep users logged in using sessions, along with so much more, and we'll find out all about this in the upcoming sections. So we're going to kick off this project in the next video, where we'll take a look at node and MPM along with installing this on our system.
3. Sk project encourage: When taking any course, it's really important to
not get in the habit of following along just for the sake of ticking
off another lecture. Take the time to
process each lesson, review the code which you write, and think about how you might approach these
solutions yourself. With this in mind, this class is project-based and this gives you the opportunity to really make something
personal and unique. You don't need to get too
lost and diverted away from the class and you can even take a step back after
you've finished the class, come back and make some
project changes afterwards. This will really give you
a good chance to practice what you've learned
away from the class. Also remember to share
your project to here on Skillshare and only
will I check it out, but it will also inspire
fellow students too. For more information
on the class project, head into the Project
and Resources tab where you can not only
upload your project, but you can also see
other class projects too. With this in mind, I look
forward to seeing what you create and upload
here on Skillshare.
4. What is Node & NPM?: Welcome back. If we head over to nodejs.org, this is the official homepage of Node.js, which we'll be using to build this final project. We see at the top there's some information about what Node.js is. Basically, Node.js is a JavaScript Runtime, built on Chrome's V8 engine. What exactly does this mean? Well, V8 is engine which makes JavaScript work inside the Google's Chrome web browser. When we've been using JavaScript so far on this course, it's been running inside the browser. Chrome uses its own V8 engine, and also other browsers have the own version too. Historically, inside of the browser is where JavaScript is run, on the client side. However though, since Node.js was developed, we can also write JavaScript code, which runs on the server too. One of the key things about V8, is that it can also run anywhere. Not just inside of Chrome, making it available to work in other applications too, which is wrote in the C++ programming language. With this, Node.js was created using the V8 engine, which will allow our JavaScript code to be run on the server sign two. This was huge for a lot of developers who already knew JavaScript, as they can now write the front-end and also the back-end of applications using the same language. Before this, we would have to learn another server side language, such as PHP. Why do we want to run our code on the server? This is because it allows us to create dynamic web pages. This means, the web page is created on the server first, and can also include any dynamic data, pulled in from our database or other sources. Also, it can check if the user is logged in, for example, before sending back any sensitive information. If you've not already done so, make sure we click on the download button for the current version of Node and go ahead and follow the installation instructions. This on our system will allow us to run Node locally and create a web server on our own computer for development. When we're downloading Node.js, it will also include npm, which is the Node's package manager. This is the collection of thousands of packages which we can use with Node to build almost any type for project which we wants. Also on [inaudible] 2 , if we build something ourselves, which we think may be useful for somebody else. We can then bundle it into what's called a module and also publish our own module two on npm. As we go through this project, we'll be using npm modules to add functionality. Such as a module called passport. We search for passport, this is a Node module which we are going to be using to provide user authentication, so users can login with a email and password. We'll also use many more npm packages too, as a progress, to allow our passwords to be encrypted, to allow file uploads, for security and many more, which will discover soon. This is Node and npm and an overview of what they can do. We'll discover more about them as we go through this project too. If your Node download is now finished, go ahead and click through the installation steps to install on your computer. Then I will see you in the next video.
5. Static v dynamic web server: You may be thinking, why are we using a web server to build projects? Haven't we gone fine so far without one? Well, this is a fair points among which I'll try to explain in this video. In a general way, we can categorize our websites into either static or dynamic. On the screen here we have our shape matching game opened up in the index.html. In all of our previous projects so far, if we open up the index.html and also opening up inside the browser, we have our HTML content. This HTML is all static as the content does not change. Yes, we may have some shapes appear in when the game starts. This is all done in the browser by using JavaScript. Inside the browser, if we go to right click and view page source, this will open up in a new tab. This is the exact same code displayed here as we see over in Visual Studio in our HTML. Even if we uploaded this project to a web server and made it live on the web, all of this content is still static and we'll get back the same results from the server. Basically in a static project like this, the server will send back what we gave it, such as the index page. Dynamic web pages on t [inaudible] he rondo can come back from the server a lot different than the code we can see in our text editor. If we go over to the finished version of our troubled projects and also open the browser. So don't worry about [inaudible] , how all this looks. We'll learn about all this code during the next few sections. I'm going to go over to one of the hotel templates, which is inside views, and then if we go to hotel.pug, let's close it up. Again, don't worry about all this complicated code. For now though, I want you to concentrate on one thing. This is the hotel name, which is in a level three heading. The syntax might look a little confusing because of this shorter h3. But this just a h3 elements we've already learned about. After this is hotel.hotel_name. If we go over to the browser, we can see the hotel's actual name, such as Hotel 11 or Hotel nine, rather than this hotel.hotel name, which we'll see in Visual Studio. If we go to the browser, we can see the hotel's actual name is also available in the source. If we go to right click Inspect and then click on our hotel name, go to the elements and struck this down. Here we can see our outputted HTML which is returned back from the server. We have our h3, which we can also see here. Both is dynamic data has now been replaced with hotel name. Here we see that the web server this time is taken our hotel template from Visual Studio and filled in the missing data with data from our database before returning back towards in the browser. This is what dynamic web pages are all about. Static sites, just like the one we've seen before with the Shape matcher, a totally fine for simple applications. Even ones which still use API data too, such as our song finder. But dynamic science is perfect when we want to change data before sending it back to the browser. Or for security related tasks, such as handling sensitive information from the user. As we don't want it to be handled inside the browser. Hope it make sense. Next we're going to get up and running with our project using the Express framework.
6. Express.js and express generator: For this project, we're going to be taking advantage of a lightweight framework for Node called Express. Express is a really popular framework in the Node community, and it's the ideal platform for us to begin our project. Over in the homepage which is expressjs.com, we can see this homepage, as it says here, is a fast, and unopinionated, and minimalist framework for Node.js. This means it does not assume how our project will be. We are free to basically take advantage of what Express provides without having to adhere to a lot of strict rules, which a lot of other frameworks force upon us. It's minimalist, which means it provides the base features we'll almost certainly need, such as a web server, route end to switch between pages, error handling and templating, boost any extra functionality is up to boost the ads by using the node package manager, which we looked at before. Express provides a web server which we can use to serve our pages, and this also serves them to all browser with the dynamic content just like we looked at in the last video. Even installed in Express 2, is done as an npm module. As we see down here, we have the terminal code to set the server as a node module, so we can set things up manually, or Express also provides a generator. We have everything we need to get going. If we go to the menu and Getting started, hover over this and then we can click on the Express generator. We need to install this via npm, so I'm going to head over to the terminal to install. Windows users can open up the PowerShell program for the stage, or any other program which you may prefer. I'm going to open up iTerm and make us a little bit bigger. Before we continue, we need to make sure Node and npm is correctly installed. We can do this by typing inside of the terminal or inside a PowerShell. We can check Node is correctly installed by typing node -v. If we see a version number here, it means the Node is correctly installed. We can do the same with npm -v. Again, if we have a version number appearing, this means that node and npm is correctly installed. If you don't see the version number or you have an error, do make sure to go ahead and re-install Node, and make sure this is in place before moving on. We can then go ahead and install the Express generator using npm. So type in npm install exepress-generator, and then dash g at the end. Mac users may also need to add the sudo keyword before if you hit an error. Just before this, we need to add sudo and then "Enter", and this will then prompt us for the password, then hit "Enter". Now is, we'll go ahead and download the Express generator from npm. The dash g flag which we add at the end of here, will install this package globally. This means it can be used in any project, not just the one we are creating. Now we need to navigate to where we want to create our projects. I'm going to be adding mine to the desktop. At present, I am in my current user directory, so I'm going to use cd to change the directory to the Desktop. Hit "Enter" and now we can see we're inside of the desktop. Of course, you can change it to be any location which you prefer, I'm just keeping mine on the desktop for ease of access. Now to create a new project in this directory, we can run this command. So express followed by the name of our project, I'm going to call mine lets-travel, and then dash dash view is equal to pug, hit "Enter" and let us go ahead and create our projects, which is also using the pug templating language, which we'll cover soon. If we scroll up inside the terminal, we can see a list of files and folders which have been created for us, and also some instructions on how to get started. I'm going to be using Visual Studio's built-in terminal from now. I also recommend you do this too throughout this course. If you prefer, you can keep on going in a separate terminal, but you will need to go ahead and first change into the current directory for the project. Then you would need to run npm install to get all the dependencies, and then start the application with these commands down at the bottom. If you are following along in Visual Studio Code, we can now close down the terminal, the browser, open up Visual Studio Code, and then drag inside our project folder. We can open up Visual Studio as built-in terminal, by going up to View in a sub menu, and then go down to Integrated Terminal. This will then open up the terminal down at the bottom, listed are our terminal commands. This also has the benefit of having our directory automatically setup to the column project folder on the desktop. We don't need to navigate to where our folder is located. We're going to take a better look around all of these files and folders soon, but for now, if we open up the package.json and then close the sidebar, inside here we see some dependencies, which inside of this dependencies object just here, which are needed for this project. These dependencies are node modules which you can install using npm install. Down in the terminal, run npm install, hit "Enter" and then give, say a few moments to run through and pulling all the packages we need from npm. npm install will grab everything we need listed inside of this package.json, and then place them inside of our projects in a node modules folder. Also, when we add more modules later, again we will use this npm install command, but followed by the module name we want to install. Great. Once this is done, this should now be a node_modules folder. If we open up the sidebar, if we open this up, we just got a drop-down, we see listed everything which we're using for this project. It will have things such as a debug module. We also have express which is just here, and also pug, our templating engine amongst others which we'll also use. Now all that is left to do, is start up our web server and open it up inside the browser. Still inside this package.json file, up at the top we have something called start inside of our scripts. We can use start script inside the terminal, to then go ahead and kickstart our application so we can view it in the browser. Down inside the terminal, let's run npm start, hit "Enter" and then open up the web browser, which you want to use. Navigate to localhost, colon 3000, hit "Enter" and now we can see our basic Express up is now running. But how do we know it's port 3000 which node is running on? Well, if we go over to the package.json, the start command has a file path of bin, forward slash, www. If we open up this file inside the same bar, inside of this file is a variable which is set to port 3000. Here we have a variable called port, which is set to be port 3000. Also, if you happen to use port 3000 for anything else, you can also change this number to be something different, such as 3001. Its file structure and setup is a little different to what we've used so far. In the next video, we'll take a better look at what we have here in our Express Generator project.
7. Express project structure: The project structure which we have here can look a bit intimidating if you're used to using static websites. However, it's not too bad once you get used to it. If we open up our sidebar on the left-hand side, I'll make it a little bit bigger. From the top we have the bin folder, which is used for any startup scripts. We touched on this in the last video where we seen these start script inside of the package.json file, which then points to this www file, which contains our startup scripts. This file inside here sets the port number we want to use, and also goes ahead and sets up a HTTP server to then serve up our projects. Under this we have our node modules folder, just down here. This contains all of the packages or modules which our application needs. Each time we install a module from MPM, it will then appear inside of here and also be listed inside of the package.json file as a dependency tool. Since these modules also listed in the package.json file, we can even remove or delete this full node modules folder, and an MPM install command will then again, add this back to our project. This is useful for things such as storing our project on GitHub, as the node modules folder can often get really big and we don't want a huge folder like this saving to GitHub or elsewhere if we don't need to. Download this, if you're closing node modules, we also have a public folder. The public folder contains our static files, such as any images, any style sheets, and also any JavaScript files or libraries for the front end. Here we can add our project images, any custom third-party libraries or frameworks such as Bootstrap CSS and JavaScript files. This folder is not for any JavaScript files or the server, such as our page templates or anything with sensitive information, such as an E configuration files. Under the public folder, we then have our routes folder. Each folder will contain one or more JavaScript files, which handle what to do when a user visits a certain URL. As an example, if we open up this index.js, and I close the sidebar, you will see the router.get, which handles the homepage. Inside a router.get, we first see the route of forward slash, so when the user visits the home router or forward slash, these enrages a function which then goes ahead and renders the index templates, which we want you use. Then after this we have a objects where we'll pass the page title of simply express, and later on we'll also pass different information such as variables to our page template too. This moves on to the views folder if we open a sidebar and then open up our views, which is a view which the user sees. It contains all page templates which are created on the server before then sending back to us. All these files have the file extension of.pug. This is because we're using the pug templating engine, which also used to be known as J2. You may come across some j references in the documentation or one searching on the web. You can use other templating languages if you prefer, such as EJS. But this will be up to you to implement if you want to make this change. If we'll open up the index.pug template. This is a template being called from the router page we've seen before, so this is the index which rendered when a user visits his home route. Block and extends, which we see at the top of the file, we will look at this soon. But it's h1 and also p elements are what we see when we visit the homepage. We have the title and then the text of welcome to, and then the title. Inside of Chrome, if open this up, this is the text we see inside of here. H1 equals title, is the title which is passed from the router. Again in index.js, this is the title we see just after we declare which template we want to use. The text of express is then being passed to our index and that's why we see the text inside the browser. Below this we have a p elements with a text of welcome to and this will be express. We can also use this as a variable inside of a string. We'll cover all of these templates in syntax in the next section. Down after the views we have the app.js file. This app.js is the app's starting point and basically the main page which connects everything together. It loads up everything we need for the project and by default import any packages we need lope at the top from the node modules folder. It also import our routes from our routes folder, which we can see here, by these file paths, and then stores them inside of a variable we see as below. It then goes ahead and kicks off our new express app instance and assigns it to a variable called app. After this we set pug view engine, which is used here, which declares which templating language we wants to use. After this we have app.use on various lines. This is used to mount any middleware to our application. We'll look a middleware in more detail later. But basically middleware is a series of actions or functions which we can run our code through. An example could be when a user logs in, we could add some middleware to validate user's information before moving along to the next stage. But again we will look more into this when we are at our own middleware to the project. If we scroll down, next up we have our routes, which again is middleware and these are the two variables which we had before, which is just about the top. These are the variables which link to our file path for the router. It would declare one wants to use our index router when the user visits any of the home routes, and we also have a default user's route too which has been setup with Express Generator, and these routes are stored inside our index and uses.js files. Scrolling further down, we also have some error handling, which again is middleware. App.use without a file path, will make these error handling functions available for all routes in our site. Finally down at the bottom we have module.exports equals app. This will make this file available in other parts of our application if needed. Back to our sidebar, the last files are the package.json files. Inside of our package.json, which we've briefly looked up. We have the information about our project, we have our startup scripts to run our web server. We can also add our own scripts here too, as a shortcut to type in out this longer command in the terminal. We also have the name which we set and also the version number which we can set too. Further down, we have the dependencies which we looked at before. These are the node modules, mid file projects and we can also add dev dependencies too, which is the modules we need for development only, and these will be ignored for production. Soon we'll be installing in the node mom package, which will be a dev dependency so I'll show you how to do that soon. If we open up the sidebar, we also have a package.json. For example, inside of our package.json, we have the Express dependency, which is just here. This says currently at the time of creating this app expresses our version 4.16.0. The tilda icon you see just at the beginning here, means we won't express version four, but we also want any minor update of versions two. Ie we can take 4.17 or 4.18 and so on. With this in mind, if we go over to this package-lock.json. And then if we do a search for Express, but then taken to the Express section of this file, we see the actual version is locked in at 4.16.3. Also, this module itself has dependencies too. If we close down this terminal and then scroll down you see we have this required section just here and express also requires a lot of different modules too, such as cookie, debug, escape HTML and also lots more. This is why when we go over to our node modules folder and open this up, there is lot more folders listed here or Lamar modules than we originally seen if we just go to the package.js. Let's close it down. All of our dependencies inside this package.js also has dependencies too and these are listed in the package-lock.json. This is an overview of what is included in the project to begin with, I know we've said it quite a few times already, but really don't get overwhelmed by all of this if it's new to you. We'll become a lot more familiar with the setup of the next few sections.
8. Using Nodemon: We are now going to install a node package, which will save us a lot of time during this project. If we go over the views inside the menu and then go to index.js, views, index.js, and then you can go ahead and make a small change to our text inside here. If we say, welcome to, and then say, let's travel. Give a save and then open the browser and then reload. We see this change has been reflected inside the browser. This is fine, and this is the result which we would expect. If now go over to a script file, such as inside the routes and then go to the index.js. Let's now make a change inside of here. The comma is out, it's res.render,/. Remember this is the line which renders our homepage or our index templates, and then instead if we go to the line below, say res.send and then the text off 'hello'. This will simply send a string of texts to the browser. Give a save and then reload and see that nothing happens. This is because if we make changes to a script file, we need to restart the server. We can do this if we go over to the terminal remember this is view, and then integrate the terminal, we can close the terminal downwards, Control C, and then we're taken back to our directory, and then again use npm start, to begin the server once more. Hit Enter, then once it's running, we can go to the browser and Hit Reload, and now the terminal has restarted the server. We'll now see the text of 'hello' has now been updated in the browser. Although this works fine, it's a bit of a pain to keep restarting the server, every time we make a change inside those files. To make things easier, we can use a package called nodemon. If we go over to nodemon.io, this is the homepage for the nodemon package, which we're going to be using. Nodemon will watch for any changes in our source code and then automatically restart the server for us. We can even install nodemon, as it suggests, using this npm install command, the name of nodemon for the package and we may also use this -g flag when installing the express generator, and it means we can use it globally in any project and not just the one we are currently working on, or alternatively, if we just want to use it in our single projects. This is simple to setup too. We can head over to the terminal in Visual Studio Code and then close down a terminal again with Control C and then run the following command against our npm install, and then this time we want to say --save-dev and in the name of our package, which is nodemon, Hit Enter and it will go ahead and grab the package from npm, save dev we'll save this in our project as a development dependency. Meaning we can use it during development, but is not required when we push the app to production. Give them moment to install and then once this is done, if we go over to the sidebar and open up the package.json. Inside of here we still have our dependencies, which we've seen earlier but further down we also have our devDependencies and they're listed as our nodemon package. Now for this to work in our app, we can create a script, just like the startup script, which we have at the top here. This one I'm going to call devstart. Add a comma after the first line, and then we can add devstart. This is the alias which we're going to give to this command. Add a colon, and then inside the quotations, we're going to add a command which is nodemon and then the same path which we have used here./bin/www. This is going to essentially run the same startup scripts but this time we're watching for any changes using nodemon. Give that a save and now we can go ahead and start our servers. This time if we go over to the terminal, make sure the server is closed down and then this time can run npm, run devstart. Of course devstart is the name of our script which we have just here. Hit enter, once it is up and running, you should see the green line of code just here and then over to the browser, hit reload and now everything should still be working. If we again go over to the index.js file and then make a change. Let's say 'hello again'. Save and as soon as we hit save, we can see the terminal at the server has restarted. Then reload the browser and now we'll see our text has been updated without us having to manually update the server. Let's now reinstate our res.render, so we can remove the res.send and then command and forward slash to now uncomment out this index page, save this, and then reload and refreshing the browser will now show these changes without having to close down the server and restart.
9. Serving static files: For this project, I have provided some images which you can also use, such as images for the hotels, the countries, and also the logo. You can of course choose your own if you prefer. The ones provided are inside this folder called images, which I have here saved onto the desktop. If you open up this folder, we can see we have the beach, which is one of the main images on the homepage. We have our logo and then our country's images and hotels organized in two folders. Let's also open up the project folder. Double-click on Let's Travel and then inside here, we can go to public, and then to images. Then if we select the four items from our images folder, and then drag these over to the images folder inside of our projects, so this is over and make sure these are inside of the images folder, rather than just the public section. We should be able now to see these images, if we go over to Visual Studio code. Open this up and then the sidebar and then inside of a public, we now have the images inside of images folder. If we go over to the index.pug, which is the main homepage templates, we can test this out by adding a image. Underneath our text of Welcome to Let's travel. We can add our image, we can add some attributes inside the quotations to set the image to use equal to our images which is the folder. Then let's go ahead and add beach.jpeg, which is this one just here. Note we don't need to add the public folder to our file path and then save this, open up Google Chrome or your browser, reloads and that's our image of the beach, great. Now all of our images and also our project structure is now in place. In the next section, we're going to dive deeper into our project by looking at routing and templating.
10. Introduction to Pug: We have already talked about how we will use templates to create our projects. These templates will be a combination of HTML, JavaScript along with any dynamic data mixed in, such as a hotel name, which we looked at in the last section. We know that we can't do all of this with the standard HTML files. So we need to use a templating language which compiles all of this into HTML. I'll be using a templating language called Pug to do this. It may look a little strange to begin with, but it's actually simpler than normal HTML tags. To see how to use Pug, let's head over to our index.pug file. There was a sidebar to a give small space, and we've already had a quick look here with the p elements and also with an [inaudible] image. We have extends and block at the top, which we'll come back to soon. For now let's concentrate on the features of pug. Generally, using Pug is just the HTML opening tag to make for the shorter syntax, just like this p element with the text of a text-based elements work in a similar way, such as add in a heading. So let's add a h1, let's say title, over to the browser, and then reload and we have a level one heading down here. Attributes are added just like with normal HTML tags. But instead with Pug they add it inside brackets. If we wanted to add a link and then at the href, we do this inside of the brackets, and then the rest is just added as normal. So let's add a link to Google. So www.google.com, we can also add our classes. So let's add a class of button, and then afterwards we add our text outside the brackets, which you want to add for the link. So link to Google text. Save this and then reload and now have our hyperlink at the bottom, and if you click on this, it will work just like normal HTML a tag. Back over to our project and the index.pug. The indentation is also really important when using Pug. In normal HTML, indentation is only really for readability. But when using Pug, it's necessary to use it to show what level each element is on. For example, if we added a div to contain this title on the link, just like this, and then if we go to the browser, and then go into developer tools would right-click and inspect, and then select this div just here. So this is a div which we just added, and the developer tool shows that the div title and also our hyperlink are all on separate lines. They effectively all at the same level, we don't have the title and the link inside of this div tags. But if we go ahead and indent the h1 and the link, and then refresh this. We now see that the div opening tag and closing tag now surrounds our title and link. So the indentation causes these two elements now to be nested inside of the parent div. Another thing about indentation inside of a pug file is only mixins, which we'll cover later, and also this block declaration at the top, and extends can be at the top level i.e. on the far left of the file. If we move over a nav element, so our link for example if we move this over to the left of the file, save this and then reload the browser. We now get a error saying only named blocks and mixins can appear at the top level of a template; This is basically because this file will be used inside of a NAV file. So this indentation keeps everything right when the HTML is compiled. Let's go ahead and reinstate this link, and clear the error. Something you may have noticed is above, we also use a equals for this title. This is because we can also include JavaScript in these Pug files too. If you remember, the title was passed from our router, which was index.js, just here inside of this title. So this is JavaScript being passed to our template in the index.pug. Therefore, we need to use the equals rather than just a plain text element, just like this and also our p elements just here. This title is also displayed in the browser sub too about the top here. Use an equals will also render to the screen the result of any JavaScript. So instead, if we say rather than title, add some JavaScript of 5 plus 12 over to the browser, we have the results of 17. But instead if we go ahead and remove the equals, you got to save and then back to the browser. This now displays as a text string of 5 plus 12 rather than outputting the sum of 5 plus 12. So let's reinstate this back to title. So you don't have to remember this but I'll put in the result like this is called buffered code. It is also unbuffered code too which does not directly add to the output; This can be any normal JavaScript with a dash prefix. So for example, we can add a dash and then add any JavaScript such as a constant of name, and it's our name to be Chris, and then this can be used where we want to place it. So h1, we can place our title with our variable of name. Since it's a JavaScript we'll also need to add the equals and test this out, and then down at the bottom there's our variable name of Chris. So this unbuffered code, it's just basically declaring line of JavaScript, and then we can go ahead and use it anywhere we want inside the templates. This JavaScript can be pretty much anything. So we could have an array of foods, cheese, eggs, and chicken, and now we have this array. It gives us a chance to take a look at the special syntax which Pug provides for creating a for loop. To do this, I'm going to add this at the bottom, indent this the same level as the link, and then create a unordered list for our foods. Indented one more level we're going to create a loop with each food in foods. So foods is the name of our array which is just here, and then each individual item in the array is going to be stored inside this food variable. So after here we can set our list item to be equal to food. Again this is JavaScript, so we need to add the equals symbol. So we don't output this as a string. Say this, and then test this out. We now have cheese, eggs, and chicken at the bottom. This output can also be mixed with an eText too. So rather than just the outputted individual food, we can add a string of I love, and then add plus food. Actually about a space just after there and try this out, and our text is also now mixed with our JavaScript variable; This is just a overview of what Pug can do, and it really is simple enough once you get used to it, we will use a lot of Pug features for this course. However, if you prefer to take a deeper look now, you can head over to Pugjs.org and take a deeper look, at the top of this file we also have this extends and also this block content section. We have not yet discussed what this means yet, but we'll go ahead and cover this in the next video.
11. Template inheritance: We have both block and extends keywords, inside the pug files at the top just here. If we open up our layout to pug file, which is available in the view section, click on layout the pug. This layout file has no extends keyword at the top. This is because it acts as the main pug templates. The files will then extend this main layout. This is why we can open up our index the pug and also our error the pug as well, which is available in the same folder, and then see the extends layout at the top. If we go back to the layout the pug file. There is also a block contents section here down at the bottom. This is what it sounds like. It's basically a block of code. Any of the files such as our index the pug can go ahead and replace this section called content. Inside of our index the pug file, we'll open this up. All this section here, basically everything from our unordered list all the way up to our title, will replaced inside of the block or inside the space which we specify inside the layout. We can also have multiple blocks too, each with a different name, such as footer or a sidebar block. These can also be added to another file, and then referenced by the block name, and the same way as his content section. We freeze, then add anything else around this block. For example, above the contents and we can add some text. Above the contents, and then below it, below the content, and now if we go to the main index page, and then reload, we see at the very top we have the text of above the contents. and then at the bottom we have the text of below the contents, and then everything in the middle, right through from the title down to all unordered list isn't provided by this block content section, which is an overridden by the contents of our index the pug. Once you understand that, we can now create sections of our code inside of a layout, and then we can outsource the contents to a separate file, skip all code, more organized, more maintainable, and we can also reuse these sections of content too in multiple files. Now let's go to the layouts, and delete these two p elements which we created, and then give that save. This layout is useful for anything we want to display in all of our pages on our site, such as adding a header and footer. We can then replace the main content down here or a new sections by adding more blocks. Now we know how our templates are laid out. Next, you will get the chance to get some practice by creating the header section.
12. Time to practice: Creating the header: When dealing with anything new, just like Prog , we really began to get better when we practice on our own. Without just following along. Here we can see the final version of the projects, which I would like you to go ahead and create this header section from the top navigation, which includes the logo and also the links at the top, right down to the main beach image just here. If you feel confident, you can also add the search form two, which is in the middle. Just as a guide for where you're going. In this, we'll need to go inside of auto-layout dot Prog file, inside of the body section. So just above this block content. So it's at the very top of the page. I'll be going over all of this in the next video. So don't worry about messing up. It's all good practice. So I would recommend you pause the video now on this final version and then it give this a go.
13. Solution: Creating the header: I hope you managed to give that a go and hopefully have some header content. Now on the screen, I'm going to go ahead and create my version of this header. If yours is different to mine, you can either change it to be the same as ones created in this video or keep yours as you would like. Either way is fine. I would maybe recommend though keeping the same class names as we use in this video so the CSS matches up later. Let's begin over any layout docktype file. In between the body section and the block content. I'm going to begin by creating the header section just like we would normally do with HTML. Nested inside within this nav element. This logo can be a link. This is so the user can click on it and then be taken back to the index page. Our a for our link, which can include the href inside of the brackets which simply links to forward slash, which is the homepage. Then one level in from our link. If we go ahead and add our image, this will then be clickable and also link to this homepage which is forward slash. Inside of the brackets we add our source, so SRC and the sequence be forward slash images. This is logo.png. We can add a class or an ID to our element. By adding this inside the brackets as a attributes or we can add it just after the element name. Add image and the harsh and logo. This will give the ID of logo for this image. Then just after this we can go ahead and create our another list which is going to be for our sign up and login links. This needs to be at the same level as our surrounding link above, so ul followed by our list items. If we want to make these links, we need to indent the a tag plus the href. We don't have these templates or these routes setup just yet but we can still go ahead and link these to sign up. With a text of sign up to and then do the same below add our next list item at the same level as the one above. Turn this into a link. This time this one is going to be for login, which is the routes of forward slash login and the text of login to. If you've created this and you've not added the href in, I would recommend you keep yours consistent with mine so it has no problems further down the line. I added forward slash login and also forward slash sign-up. That give us save name over to the browser. There's a logo and then our two links at the top too. The next thing to do is to create our form input for our search. Which is where we can type in the destination, the duration, the departure dates and also the number of guests that we want to search for, so we do this just with a simple HTML form. This form is going to be in a surrounding div. This div needs to be on the same level as our nav element. Come down for the nav element and then begin this at the same level. When creating a div with the class, we can even create the element just like this and then add the class name as a attribute or instead we can have a shorthand which is simply dot and the name of our class. If we wanted a div with the class of search nav, we would just do it simply like this. This will then be outputted as a div with this class. Then inside we can nest our form. This form is going to be made up of a series of form input and each one is going to have a surrounding div of input underscore wrapper. This will make each input block level, so they appear on their own line. Then we need to label for our first one which is going to be destination. We add the for attribute or destination and then a text of destination. Then we can add our input, the type of text, the idea of destination which will match this label just here. The name of destination too. I will just change the style so it's a little bit more readable and then the required attribute on the very end. Save this and then let's see how it's looking in the browser and now we have our destination form input. Now this is working, we can go ahead and copy this input_wrapper and then paste this in and make sure this is at the same level as above. This one is going to be for the duration. Change the label to duration, the text and then we can add nights inside here because we're going to be sign the duration per night. The input type of text is fine, the ID needs to match alteration and the name of duration too. This field is also required so we can leave this in too. The next one is going to be for the departure date so the label text. This time the input is going to have the type of date so it drops down as a date picker. The ID of departure date too and then the name, date of departure. This is going to be CamelCase. This field is also required just like the others. I'll do this the same one more time this one is going to be a number field. The input type of number because this is going to be for the number of guests, so number-guests. The name of number of guests and then the ID which must match this label of number guests. I have a typo though so change that. Then finally the name, again in CamelCase of number of guests. Let's check this out in the browser. Great, that's our four inputs. We can see if we click on the date, we now have a drop-down date picker. It should be a input type number which is on our two text fields at the top. Back to our layout, after these first four input we're now going to change this up. We need a couple more and one is going to be for the star rating. This is going to be a select input with different options through from one to five. We are then going to have an input with a select again, which is going to be for sorting the price from low to high or high to low. Then finally the Submit button at the end is going to use the same input wrapper, so.input_wrapper. This is a select with a name equal to stars. Remember from earlier the select input do need to have an option to select. Add our first option inside here with a value to then pass to the server once selected. It's just going to be simply a number for the number stars and the text of min one star. We can go ahead and copy this in and paste in four more times. The second one is for two-star, 3, 4 and 5 and also the same for the text too. The last input, again these ones have the same input_wrapper so we keep the styling consistent. This will also be a select and this is for the price sorting from low to high or high to low. We can add the name of the select to be sort. Paste in the option with a value of one. This is going to for the price set from low to high. We're also going to paste in the same but this time option can have a value of negative one. This is because these are the values which you required when searching Mongo database to then sort the return queries from high to low or low to high. We'll see this in more detail later on. This can be also change. This time let's see price from high to low. The last input also needs the input wrapper and this is the Submit button. Button with the type of submit and then the text of search.That should lead us now to form. Let's check for any errors. The star rating from one through to five. We have the price from low to high and high to low. Then I will submit button at the bottom. We also want this beach image be part of the header too. Inside this header section, just after the form, we need to make sure this image is indented at the same level as our navigation. If we scroll up and then go to our search nav and keep an eye on this line which we see in the text editor. Now we could start our image, so img. The source just as before, is going to be equal to forward slash images which is inside the public folder, the beach d.JPEG. Then let's bring up the block contents. Save and then reload the browser. Now, we have the image inside the header. We also have some cleanup work to do just below and this is only consent from the index.pugfile. Go over to index.pug. Then we can remove our examples from earlier. We can move out another list, our links and array. We don't need the image. We don't need the text or the title. Let's just leave in place the extends layouts and the block contents and off to the browser. Now, we just have the header content from our layout file. There we go. There is our header content now in place. Next we'll move on looking at using mix-ins, which are a great way to reuse the same code in multiple templates.
14. Mixins: One of the things we usually want to avoid when coding is repetition. If we reuse the same code more than once, it often makes sense to do things a little differently. Thankfully, Vue gives us the opportunity to use what is called mixins. If we look at the finished version here. Take our hotels here on the home page, for example. The same hotel structure which you see just here. Also the layout is used if we perform a search tool. Here we have our listed hotels which follow a same pattern. If you do a search and filling all the fields just on here, hit search. Where you see our search results follow a similar layout and pattern as our hotels on the home page. This is a good use case for a mixins even though the hotel data is different, such as the description and also the title, we can make parts of the mixins dynamic. It's really flexible to use. Over in the index dot pug file inside of Visual Studio Code, let's begin by adding the hotel info we need to begin. Starting with the wrappers and then link to all hotels. Just have block content, make sure you have this intended. Then we can add our outer div. Outer underscore wrapper. Inside here we also want to have a second wrapper for each individual hotel. Hotel underscore wrapper. Then the h2 of hotels. This level two heading is also going to have a link. Just after this we are going to add our link, which is going to link to all hotels which are available. Add a href, which is going to link to forward slash all. Then in brackets a text of "see all" you're going to save this and then go to the browser in the home page and then refresh. Down at the bottom we have our title of hotels and then we'll have a link which we'll wire it up later to see all the available hotels in the database. The reason we have this link to see all hotels, is because the hotels which will display on the home page are only going to be restricted to nine random hotels pulled in from the database. This is so the home page doesn't get too crowded when we have lots of hotels inside of our database. Now go ahead and add a dummy hotel. You can see how this is looking. After our links and at the same level as our h2, we can add a hotel wrapper, so dot hotel nest. Inside here we're going to have a couple of different sections. The first one is going to be for the hotel image. Hotel img. This is going to be the section which is going to appear on the left hand side and it's going to have a image of the hotel which we'll then link to the full hotel description. We need a link with the href. We can leave this empty for now because this data is going to be dynamic and it's going to link to the current hotel which we're viewing. After here we're going to add a image which is the main hotel image and we could just add a dummy image now. Forward slash images forward slash hotels. Then we can select any hotel we want from our public folder. Let's drop hotels down and you can choose any one of these images from there. I'm just going to go for hotel one dot JPEG, and just like this link above, this will also be dynamic too and the information will be pulled in from the database and then we can grab the correct image. After this hotel image section, let's add a new section surrounded by a div called hotel underscore info. This is going to contain information about the hotel, such as the hotel name, the star rating, the country, and also the price per night. The hotel name is also going to be a link just like this image, which will also link to the full hotel view, which will show a extended description. Add our surrounding link with an empty href for now. Our title of the hotel is going to go in a h3 element. We can add some dummy text of hotel one. A horizontal rule then separates the title from the rest of the information. Now it's just a case of adding some p elements. The first one is for the star rating. We can set it to be anything we want for now. Let's go for four. The country: Jamaica. Then finally the cost per night. Remember this is just some dummy data so we can see the structure. Then let's see how this is looking. There's our image section above, and then our hotel info with the name, star rating, the country, and also the price. Great. We now have a hotel on the Home page. We also need to create a view to link to you when the user clicks on this all hotels link. Let's go over to Visual Studio, open up the sidebar, and then inside the views we can create our new view called all underscore hotels dot pug. Inside of this template we also need to extend layout. We also need to add block contents. Now as you going to share a similar view to the hotel from the Home page. If we go over to the index dot pug and it will copy all the way from the cost per night right up to the hotel. This is the individual hotel div. Go to all hotels and then we can add our wrapper. So dot hotel underscore wrapper, indent it in one level and then we can paste in the code from the last video. Make sure this is just indented in one level and make sure our all hotels dot pug file is saved. If we go over to our home page, we now won't be able to see the view down at the very bottom. It is because we have not yet set up the routing to handle this forward slash all. But you can already see that we've repeated the same hotel code, both inside the all hotels template and also inside of the index dot pug. Now's our opportunity to reduce our code by moving this hotel into a mixin inside of the views folder, inside the sidebar. Let's create a new folder called mixins. Inside our mixins folder, I'm going to create a new file which is called underscore hotel dot pug and this is going to be the file for our mixin. I know we'd like to start a mixin name with a underscore but this is totally up to you. We begin by using the mixin keyword followed by a name we want to give to this mixin. Mixin hotel then we can go and copy over the hotel code. We already have in our index dot pug. We have this hotel all the way down to our star rating and cost per night. Copy this. Then if we go to our mixin which is underscore hotel. Paste this in and make sure this is indented correctly. One level layer, and then one further level for our image and info. Save this file and that's all we need to do to create our mixin. Now, index dot pug file, we then need to include this mixin file, which we just created. After extends layout. We can also include our mixin by adding the file path. Mixins, which is the folder name, forward slash underscore hotel. We don't need to add the dot pug extension. Then we need to remove the hotel code and then replace it with the name we gave the mixin. Cut or delete this hotel section from the very bottom and then we add our mixin with plus hotel. This hotel name is the name we gave to the mixin inside the file. Underscore hotel dot pug. This is a name which you set right at the top. If we now save this and then over to the index page by clicking on the logo and I scroll down, you can see we have a problem with the indentations. Let's go and check this out and this is an index dot pug, over to our index dot pug, and it looks as though this is not quite lined up with this line. These little small errors are things we need to watch out for with the indentation. Make sure everything works correctly. Let's try reloading this. There's our hotel back in the home page. But this time pulled in from a mixin. Now we can do the same with the all hotels page inside the all hotels dot pug file. We can also include the mixin in the same way as the index. After our extends declaration, we can include our mixin, which again is the file path of mixins, forward slash underscore hotel. Now we can replace this hotel code from dot hotel right down to the cost per night. Then replace this with the mixin using plus hotel. Now we've replaced a section of code from two files and added it into one mixin. We will come back to mixin soon by also passing in data to them about each hotel in the database. Along with also creating more mixins to you, as we go. Our code is now a little shorter now by including this and it will be used a few more times in this project too. Next, we're going to move on to routing and how we can use it to switch between pages in our application.
15. Basic routing: If we go over to Visual Studio Code, and at the moment, if you go inside of the routes folder, and then click on the index.js, we only have one route currently setup, and this is for the homepage. Now we're going to look at how to add more routes too and if routing will allow us to handle what happens when a URL is visited. First, I'm not going to be using the users.js file, which has been included with the express generator. So we can go down and delete users.js, so we can this delete this from our project. Then inside of the app.js, click on the main file here. We also have two references to this user's file, which we can also delete. First of all, we can remove this variable, which points to the user's routes. So delete this, leaving just our main index file. Then a little bit further down, we'll have a app.use which sets we want to use this user's router on the user's file path just here we. So we can also remove this. Now back over to our router index.js file. Let's close down some of these tabs. At the top of this file we have two variables. We have an Express variable and also a route variable here. We have these so we can use the Router functionality which comes with Express. First of all, we require Express, which is a node package and this is inside of the node modules folder. If we require any packages from inside of the node modules, which is just here, we just need to reference it by the module name. If we are requiring any of the file which is not inside the node modules folder, we will need to add the full file path before the name. After setting our Express variable, we then set to open Express router instance, and store it in a router variable. This router variable is also used down here, and also any future routes too. We then use.get here because we are handling a get request. Remember when a user visits any page in the browser, this is a get request. In the last video we added a link to forward slash all. We added this over in our index.pug template, just here. This was to provide a link to all hotels. But we've not handled the route just yet. We go over to index page and select this link. We see we have taken it to forward slash all, but down at the bottom we have a message of Not_Found. We see all the header content just a little still because this content has been added to main layout file. So now we can go ahead and write our own route to handle the forward slash all hotels routes. So back in the index.js, where we're going to be handling all of our routes, we can start just like above with router and this is a get request so we use.get. Inside here, we want this to apply to the forward slash all routes, and other function as the second parameter. So function which takes in the request, and also the response objects. These two variable names can be anything which you prefer. Then open up the curly braces and add a semicolon at the end. Request is an object containing all the information from the HTTP request. As an example, we'll use this soon to access data from within a form, which is passed along with the request. Response, on the other hand, is what we want to send back when we get a request. So request is the data coming into the server, and then response is a response from the server. If you take a look above out-of-home routes, We have res.render a page template as a response from the server. We also looked at res.send earlier too. Here we can also render our all hotels template which we created just here. So inside of router.gets/all we can say, res.render, pass in our templates of all_hotels. Then as an object, we can pass in our title of all hotels. The semicolon at the end here. Now we can test this by going over to the browser, hit "Save", and then refresh. Make sure you only foward/all routes and then scroll down to the bottom. Now we can see this hotel, which we added as a mix in in the last video. Let's also test this once more by going over to the homepage, by clicking on the "Logo", clicking on "See All", and once again we can see this is still working. So now we have two routes in place. One for all hotels and one for the homepage. This is a basic introduction to handling routes with Express. In the next video we'll look at passing data for the URL, when we covert routes parameters.
16. Route parameters: One of the things we have to deal with when routing is that we don't always know what the exact URL will be. What I mean is if we imagine a user's route, each user has a unique user ID. This could be something such as our localhost or our website URL, forward slash users. Then forward slash a username, which could be pretty much anything. This user section that we have just here, probably won't be known in advance to the developer. This section is something which we need to handle beforehand. For this, we can use route parameters to create a dynamic segments in the URL. If we go over to the index.js, which handles all of our routes, let's start by duplicating our forward slash all routes in the last video. Copy this section and then add it in just below. We can then tell express router which parts of the URL we want to be dynamic. After forward slash all, we can then go ahead and add forward slash and then create a dynamic segments using a colon. Then a name we want to give to this section. This name can be anything of our choosing. Then inside of our function just below, we can then access the name data from our URL. We use the request object. Remember we said that this holds information from the HTTP requests. First of all, we can access the request objects, then.params to access the data in the URL parameters, followed by this name variable were added after the colon. So request.params.name. Then if we saw this inside of a constance, so const name is equal to our parameters. This data can also be passed to the templates to use along with the page title. After the title of all hotels, add a comma. Then we can also pass in this name data. This name data is passed to the all hotels templates along with this name. So click on our all hotels.pug file. Then this variable can easily be accessed in the template just by referencing the name. Down at the bottom of the file, p equals our name variable. Then over to the browser. Now if we go to forward slash users and then forward slash, we can add any name into here, hit enter. In fact, this was all an increase and hit enter. Down at the bottom we now see I would name of Chris, which we passed in. We can try this again with anything we want to add. This data is then grabbed from our URL, stored inside the request.params objects. Then we can pass is now down to our template to use in any way which we choose. We can also add as many dynamic segments as we need in the URL. Rather than just having one in index.js, just like this. We can also add this dynamic segment into any section we wanted. We could also grab the age and store it in an age variable. But this all depends on what type of website you are creating. Another thing we can also do if we don't want to grab the data inside of the routes, we can also just add a star. Just like this, we could add a star and then this route, just here, all this function will then run every time a route follows this forward slash all, and then forward slash any data after this. This star can be placed in any section of the URL which you want. This will be useful for handling our user names, even if we didn't want to grab the actual data and store it in a variable. I'm now going to remove this code from the video. Inside of all routes, let's remove the router.get section, as we don't need this for this project. Then all hotels.pug, all we need to remove is p equals name, and restore this box how it was. But we'll come back to route parameters quite a bit later on in this project, where we'll be passing in any hotel IDs along with booking information.
17. MVC Pattern: We're going to be structuring this project using what is known as the MVC pattern. MVC stands for Model View Controller and it's basically a way of separating our logic into different parts. At the top of the diagram here we see the model, the model defines how our data should be structured. As an example, our hotel information will have a model and this model will stay at that each hotel needs to have a name. It's name must be present, and also a string which no longer referred to coat Islam. Also our hotel model must have a description, an image, a price, and so on. Next up and probably the most easy to grasp is the view. The view, which as it sounds is basically the user interface. We've already touched on this in the section where we've created a page templates so this should be fairly straightforward. Then there is the controller. This controller can update the view or send data to the model. A typical use for a controller in our project will be to search our hotel model for matches based on a country, then it's data can then be passed to the template or to the view. We've actually already done something similar in the last video, where we looked at route parameters. Instead of the routes index or js file, we use a controller to get the name from the URL, then we passed this name to the view to show on the homepage. We'll also be using the controller to do other things too, such as form validation when a user signs up before we push this data to the users model. This is a pattern which will be following for the rest of these projects. It's keeping nicely structured and organized. It may seem a little confusing to begin but we will get lots of practice and it will soon become clear. In the next video, we're going to be starting this separation process by creating our controllers.
18. Using controllers: At the moment we are handling overall logic inside of this routers index.js file. We navigate into a certain route and then all the logic is handled inside of this function and also this one which is below. This works fine for small applications, but we will soon outgrow this type of setup. I'm going to separate this logic inside and create a controllers folder in the root of all projects. Let's go over to the sidebar and create a new folder called controllers. Make sure this is at the same level as all level folders, such as the bin and the node modules. Inside here we can add a new JavaScript file for each controller which you want to create. Let's click on controllers and then create a new file. I'm going to create separate controller for the hotel related logic. Then one later on for the users to keep things organized. Let's begin by creating hotel controller.js. Controller with a capital C and then hit "Enter." This file will be a series of functions and we'll basically be outsourcing this function section from our router. We're going to take this logic, put it into our controller, and then it references inside of our router. Let's begin in our hotel controller.js and begin with A exports. This will allow this code to be available in other parts of our application. We then give this exported function a name of our choice. Try to keep it descriptive though so we know what each one will be doing. Let's call this the homepage, and then set this to a function. This is a function which takes in the request and response objects. Let's add these in, so we have full access to them inside of our function. Then if we go over to our index.js, we can then cut out the res.render from our home routes and then add it to our controller and now would also be a good time to change the page title. Let's set this to "Let's travel." We now have our logic separated, so we can now go over to our routers index.js file. We can remove this function leaving in just the initial routes and then we need to reference our host cell controller.homepage. Let's add hotel controller.homepage and then give that a save. If we now go over to the browser and then hit "Reload," and go to the homepage, we now see a message saying the site can't be reached. If we go to Visual Studio and then open up the terminal, we can see that the app has crushed. This is because we need to require this hotel controller file before we can access it. Currently, we're trying to access the hotel controller, but we've not yet imported this file. To do this, let's go to the top of our file and then we can add a comment. So require controllers and then set up a constant, so const hotel controller, which matches this name just here and then we can require the file. We need to add the file path because this is not inside of the node modules folder. This is inside of the controllers folder. So controllers, forward slash hotel controller and now hit "Save." We should now see the app has reloaded. We now have the green text. Over to the browser, and the app is now working in once more. Then we can do the same with the all hotels route too. First, let's go over to the hotel controller and create this. So just like before, we can say exports.listallhotels. Except the E function which takes in the request, the response and then over to a router. We can take the res.render from these, forward slash all. Paste this in, and then references function inside of our router. So remove the function from before. Again, we want the hotel controller file. But this time we want list all hotels. Now, let's try this router on the browser. So the home route forward slash all, scroll down and this still works to. So at the moment we've not gained a lot by having separate controllers. But these controllers will soon become more complex. Especially when we start dealing with databases and obtain the views, along with checking any user data for validation. It makes sense to start separating things from the start, so things don't get too messy as our project grows.
19. Using middleware: Middleware is a really important concept to learn, and it is used quite heavily when building Express apps. While we will be using middleware for his projects, I just wanted to give you a quick introduction first, so we at least understand the basics. If we go over to Visual Studio then open up our hotel controller, we have the request and response objects for each router. Request is the data coming in and response is the data which is returned from the server. We can use middleware in-between to basically change our data or do something with it. Basically, a middleware acts like a series of functions which we pass through. An example of its use in our project, will be when a user signs up. Inside this hotelController.js file, I want to show you a quick example and these examples are just for demonstration purposes. You don't need to follow along if you don't want. Let's make some more space and I'm going to create two more functions. Exports, sign-up, passing in the request and response objects. The sign-up section is going to handle the data validation. This will validate any user info as it comes in then this second one is going to be the functionality to login. Exports.login, passing your request and response and this is going to handle the login. Let's say we want to have the functionality to first sign up the user. The sign-up section also validates the user details then follows up by login, the user in. This is usually the functionality we want. When we sign up for new websites, we often want to be immediately logged in. All of the code to handle both of these sections could be done inside of one single controller. But for this case, it makes sense to break things up into separate actions. This is because not only do we want to sign up and then login straight away, we also want the functionality for this login to be separate. Just when a user has returned and then just wants to login. Let's avoid repeating the same login code more than once. How does Express know we want to run the sign-up function then followed by the login? First of all, we need to pass in a third argument to our function. We usually call this variable next, with their needs call next inside of our function body, when we are ready to move on to the next piece of middleware, so we call next inside here. When we want it to move down the chain onto the next piece of middleware. There is still one more thing to do. Although we call next inside this function, it doesn't automatically know what the next piece of middleware is. All that we know we want to move on to login, Express doesn't know this by default. We do this by declaring the order of middleware inside the router file over to the router index.js. We can then set up a new example routes, we've router.gets. Let's create a sign-up routes with the forward slash and then we can add our controllers. The hotelController.sign-Up. If you just wanted to use this sign-up section, we would just use a like this. We can add more than one. We can then follow up with hotelController.login, and we can pass in as many of these as you want to use and then these executed in sequence, each one requiring the next call inside the function. First of all, we will use the sign-up function, then next we'll pass it over to the login function and since these are now separate functions, this makes sense when we want to create a route just for login into. If we have, router.gets, when we get to this stage, we also want a login routes and then we can reuse hotelController.login. Now we're using the same login functionality without repeating the same code in multiple functions. We can see this is working by adding some console logs to all middleware over to the controller, just before next we can do a console log, and then a simple message of sign-up middleware, lowercase c, then copy this then add this into our login. Changes to be login middleware, save our files and and over to the projects. We need to go to our up routes, which we set just here. Forward slash sign-up, at this end then hit enter. Now only sign-up routes over to the console and make some more space. We now see inside the console at the bottom we have sign-up middleware called first, which you'd expect because this is called first and the controller then we have the login middleware just afterwards. This means that the router is now passing through both pieces of our middleware in the correct sequence. If we go ahead though, and comments sound is next function, save the file then reload the browser on the sign-up routes. Over in a terminal we now see that only the sign-up middleware has run. The code has got to this section just here then it hasn't been passed to the next piece of middleware because we haven't called next. This is a basic introduction to how middleware works. I'm going to remove these two functions as we don't need these in our projects. They just for demonstration and also the two new routes to set-up in the index.js and then check this working okay. Head to the home routes and we're now back to normal. We can load these routes in later on when we come back to this part of the app. Finally, we can also set a middleware to use across the whole site, rather than just one specific route over in the app.js, which is the main file. Click on this then scroll down to the section with app.use, which is just here. Here you can see app.use, and this is where we can set up any middleware such as cookie parser to use for all routes. Below we can see we have the index router setup here too. Both for this and all of the middleware, we can first add a route as the first argument to restrict the middleware to only apply to this particular routes. There is also many of the uses for middleware too, such as third party plugins and we will look at these more throughout this project.
20. Important Update: mLab now part of Mongo: As we progress through the following project, we use a hosted database on a service called mLab. MLab is a fully managed Cloud database service which we use to host our Mongo database. As we can see on the homepage here, mLab has since being acquired by Mongo itself. So we now need to use this service in its place. The service is called Mongo Atlas and it's a pretty straightforward swap since we just set up a database in a similar way and then use the connection string provided in our projects. Mongo Atlas is also free to sign up for and it has a free tier for development too. So let's start by going over to mongodb.com/cloud/atlas. If you've used Atlas before and already have an account, you can go ahead and login. If not, you'll need to register first before we can create our first database cluster. So if you need to go ahead and sign up, but I'm going to go down and login to Atlas now. Then once you're all setup and registered, we need to go ahead and create a new cluster. Create a new cluster may pop up as part of the registration process. So let's go ahead and build a new cluster which is going to allow us to select our region or our plan. There is some pre-configured options and you can leave the defaults as they are or change to your closest region. So I'm going to select the closest regions me. Also keep an eye out for the free tier label, which isn't in each one. Everything else, I'm going to keep us default. Just make sure you see the cost down at the bottom to be free. Then we can go ahead and create our cluster by clicking on the green button. A new cluster can take a few minutes to get set up. So I'm going to go away now and come back as soon as this is all done. So this is my cluster all now setup and the next step is to create a new user. If we go to the security tab and then go to add new user, this user is going to be for ourselves. So we can select the atlas Admin, which is one of the options on the privileges. You can of course add more users with different permissions at a later date if you need to. Then add a username and password of your choice. I'm going to add a username and a password and then go ahead and add our user. The next step is to add our own computers' IP address to Mongo's whitelist. This is a security feature so only our machine is granted permission to access our cluster. You may need to bear this in mind at a later stage when deploying the application to other services. So let's go on to the Security tab and click on 'IP whitelist'. Add IP address. We get a pop-up and we can confirm that we want to use our current IP address by clicking this button here. Then we'll get the IP address added and this field follows. So we can just go ahead and confirm. You may need to give this a few moments to get setup if you see this pending spinner just here. If you've not used Mongo Atlas before, we can check the contents of our database by clicking on the collections button. The collections button is in the overview tab and then collections. This gives us access to all of the collections which our database has and we can interact with our data. Add new fields, add new collections and see all the information which is stored in our database. Of course, we don't have any data just yet, but this is where you can access all of our database information, such as any hotels and users, which we'll go ahead and save from our project. Finally, we need a connection string. If we go to overview and then click on 'Connect'. The option we want to use is connect your application. There is also options here to use MongoDB Compass and also connect with the Mongo Shell. Both connect your application is the one we need for now. We also need to change the driver version to be version two. Then we see our connection string is displayed just here and we can also copy this to the clipboard. This connection string is the one which we'll use in our project in place of the one provided by mLab. This is done for this update. Just keep this open in the browser and you can use it in the next video when we connect to our database. Also just bear in mind as you go through the course, you will need to go through these Mongo Atlas surveys to see your data rather than mLab as I occasionally do in the class. All you need to do to see this is click on the collections button us we looked at before.
21. Getting started with Mongo: In this project, we're going to be dealing with quite a bit of information. We'll have information about how hotels we have and uses any orders which you placed and also any session data which we'll get into later. Of course, so we need somewhere to store all of this data, and I'm going to be using a MongoDB database, in this course, which is really popular database for Node applications, along with many of this too. MongoDB allows us to store our data in a JSON-like format. This makes it really easy to work with, especially when we used to JavaScript type applications. We've already worked with JSON data, so this should be fairly familiar. It also makes learning things easier too. There's also lots of helpful queries which it makes available. The query is a search on our database, and Mongo makes this really easy. We have lots of different ways we can perform searches to only get the exact data we need. For example, one of the queries which we'll be using is to search the database using a search term which they use Enter and then also filtered out by the star rating and availability, and then finally sought the results in price order. We'll also look at lots of queries too unit section. MongoDB is also free and open source too. I will also be using a hosted version in this course, which is also free. The hosted version is going to be over mLab, which you can find our mLab.com. This hosted solution is also known as a database, as a service. It basically allows us to quickly get going with MongoDB. Our database is then hosted in the cloud photos. You can also set Mongo up locally, we have in our database already hosted is really useful for later when we push our application to be live server. It also saves the configuration. The first thing we need to do is sign up for a user accounts, so if you don't already have an account with mLab, I would suggest you go ahead and pause the video and then sign-up now. I already have an account with mLab, so I'm going to go ahead and login, so add in the login details. Once we're logged in, we can now go ahead and create a new MongoDB deployments. Go to the top, and click create new, then we have some options to select, I'll be using Amazon for the provider, so click on this, and then the free sandbox plan, which is fine for development and learning. Select this and select a region. Amazon is hosted in multiple locations across the world, we only have a few options for is sandbox plan, so AWS will be fine. We can continue, and we can select one of these. Then continue again, and then we can add our database name. I'm going to call mine, let's-travel. Continue. We can review any of this and then hit Submit Order, and there we go that's our let's-travel database on our setup. Our database is empty, so it doesn't have any collections. Just gets, if we click on this, we can then take a look inside. Under the Collection Tab we see we don't have any at this time, and the collection is just a way of grouping our documents, which we store, as an example, we have a hotel's collection to store our hotel records, a user's collection, to store assigned abuses and so on. The top, we also have a URI which we can use in our project to connect to this database. There is a space in this URI for our user and also a password, so we need to go ahead and set this up under the user's tab. Click on this and then go to Add database user. We can add a username and password, so I'm going to just call mine travel as username, and traveled on as the password. Create our user, great. Now have the username and password to fill in these blanks. But first there is one more thing which needs setup, and this is mongoose. js. We are free to use Mongo directly if you want, but I'm going to be using a nose package called Mongoose instead. Mongoose basically allows us to give our data some structure in which is called a schema. If we write directly to Mongo, you can make a mistake pretty easily. If we do not set up how our data should be, Mongoose will allow us to set how our data would look. For example, will have a hotel schema, and this will have fields such as a name and description. We can now answer the datatype, each field should be, such as a string, and also restrict the minimum and maximum characters it should be, and also if the field is to be required and so on. Basically it stops us from doing things such as entry and a text string into a price field, which should be a number. This package will also allow us to create our model, which is the model we talked about when we looked at the MVC pattern, given our data some consistency. Let's go ahead and install Mongoose in our application. Let's close the terminal down with Control C. Then type in npm i mongoose. Notice this time using the shorter i command, this is just a shorthand for install and if works fine. Hit "Enter" give it some moment to pull-in from mpm. I'm checking whether this is all okay by opening up our package.json, and in the dependencies will now see we have Mongoose listed here. Over a in our app.js, we can now set up our connection. Again in the sidebar we can open up app.js. Then close this, so the first thing we need to do is to setup our connection. We need to first require the Mongoose package, which we just imported. Just after our import, I'm going to use a constant, and it doesn't matter if you use a constant or a variable, but I'm going to use the constants from now on, so const Mongoose equals require Mongoose, and remember if we're requiring a package from the node modules folder, we just simply reference it by the package name rather than adding a file path which leads to it. Then we can set up our connection using the connect method, so that's first access Mongoose. Scroll down on the app.set. Let's setup our Mongoose connection, so Mongoose variable.connect and then open up the brackets. Inside of connect, we can pass in our connection URI from mLab,so back out to mLab, copy the full URI, and as a string, open the quotations and then paste this in. Rather, we need to change our username and password, so remove the user and our username was travel and the password was travel on. Of course, when dealing with databases, you want a more secure password, but this is just for demonstration. Also have NSURI in here is not the best way to do this, but we'll move this to somewhere more suitable later. On the next line on the Mongoose. connect, we need to set Mongoose. Promise, P, this is going to be equal to the global.Promise, P again. Once we start to query our database, we need to deal with the information which is returned back to us. In earlier versions of Mongoose, we used a callback based setup, but now we can make use of promises which are lot simpler and easier to maintain. We can set Mongo to use the Promise library such as blueband, if we wanted to, which we can get as an NPM module, or I'm going to set it to be the global. Promise you have here, which allows us to make use of the native Promises available in ES6 rather than installing another node module. Next, under this, we can check for any connection errors. We first check, we have a Mongoose connection, and then once we do, we can call.on,.on is a node method which adds an event listener. In our case, we want to list now for any errors, so compositing error as the first parameter, and then as a second argument, passing a callback function to display this error. Let's create a function where we're passing error, which is going to output any error messages to the web console. We do this wave console.error, and can passing this error.message. Pass our message to the console, so let's give this a safe and a semicolon at the ends and then over to the browser or to our index and then reload, and in fact we need to restart our terminal after install Mongoose, so let's go to the terminal and then run npm run devstart, wave it to kick in and then reload the browser and check everything it still works okay. If you don't see any error messages inside of Visual Studio's terminal this should be all now setup. If you have an error do check for any typos and also check that your database URI matches the one on mLab along with the correct user and password, and once this is working and you're stage, you will now good to move on to creating our Mongoose model.
22. Mongoose models: We've already talked a little about what models, we are going to using a Mongoose sets up our models, and this will keep our data structured, so there is less chance of messing up. These models are responsible for creating data before sending off to the database along with written documents from the database too. Over in our project, we can begin by creating a models folder to keep our code organized. Open up the sidebar and then the root of the project, create New Folder called Models. Now we have models and our views, and our controllers folders and this is the MVC pattern we talked about earlier. This model we're creating is going to be for our hotels. This will have the structure for our hotel name, hotel description, star rating, and so on. Inside its models folder, create a New File, call it hotel.js. Since we use mongoose for our schema, we need to require the mongoose module. Let's close on these files down and concentrates on hotel.js. Inside here we create our constant of mongoose and require the mongoose package, semicolon and then we can create our hotel schema, so const hotelSchema, and this is equal to a new mongoose.Schema with a capital S. The Schema will map or match to the data inside of our database, therefore, what we aren't in here will shape how our database data will be constructed. The schema takes in an object inside the brackets, where we can begin to construct how each hotel would look. We need a hotel name, so let's add our first phoneme of hotel_name. Now we can set this up as an object, we want the hotel name to be of type. This is going to be a string separated by commas. We can also add some more restrictions to this data, we can also set if this is to be required, this can be a boolean value of true or false. If the field must be present or instead, we can simply add a string with a message if this field is missing, such as hotel name is required. I come on to the end. Then we can set the maximum number of characters to be 32. Then finally, we're going to set the trim to be a boolean value of true. We'll remove any wide spaces from the field from the beginning and end, leaving just the characters which answers. This is the schema for our hotel name, and we can go ahead and add a similar setup for our description. Separated by a comma, we can ask the hotel_description. The description again should be type of string required. Again, we can set this to be true or instead we can pass in a string which will be returned if the field is missing, so hotel description is required. We can also trim any wide space, set this to be true. Now we have the hotel name and the description, separate this with a comma, we now have a field for the image. The image is going to be a name, so it's going to be a string for now, we will come back to this image later on in the course because it's one or two things we need to deal with to get this working correctly. Next up we have the star rating, the star rating is going to be a type of number. We also want this to be required like all the rest of the fields. We can say hotel star rating is required, add a common onto the ends. Then we set the maximum value to be five, because we only want the star rating to between one and five. After the star rating, it's the country, the country is a type of string. This is also required, the text of country is required, the comma after required. We also want to finish, so this is a boolean value of true. That's all we need at the moment for our country. After the country, we want to set the cost per night. Cost per night is going to be the type of number, as this will be a price, and also want this to be required. With a string of cost per night, is required. The last piece of information we want to store in our hotel model is available. This is so the unmanned concise if this Hotel is currently available for sale and this will be a boolean value of true or false. Let's set the type to be boolean and the only other field we need is required, so availability is required. Once you've gone ahead and typed all this out, right at the very bottom. The final thing we want to do is export our model so we can use it in other files. Down at the bottom must create a comment of export model. We do this with module dot exports, answer this to mongoose.model, the first volume we want to add is hotel. Hotel is a name I'm going to give to this model and separated by a comma, we also pass in the hotel schema, which we just created. Good, this is now how our hotel would be constructed. Now we've setup our model. It means nobody could just go ahead and add any fields how they like. We now have a strict set of rules which each hotel must abide by. Later we'll add another model for our user too and also for any orders. We can also shape how they will look too. For now though, we're going to stick with the hotels. The next video is all about using this model to create our add new hotel phone, so we can adds new hotels to our database.
23. Creating our hotel upload form: Before we can push new hotels to the database, we need to create a HTML form, so we can add the details we want to enter. This formal parts of our admin section. So let's add this admin route first over in the index.js. Open the sidebar, into the routes, and then index.js. I'm going to go down to the bottom of our router just for the export. Let's make a comment, and I will say "ADMIN Routes". The first one we're going to use is router.get, since this is a GET request. We want this to be for the forward slash admin routes. Then we want to run our hotelController, I'm going to call it call it the adminPage, semicolon at the end. As we can see with the red area down at the bottom, we don't yet have this adminPage in the control yet, so this should be next. Open the sidebar and go to our hotelController.js file, and then we can add this right at the bottom. Exports towards adminPage, it's going to be equal to our function, which takes in the request and response objects. Then inside the function body, what we're going to do is a res.render, this is going to render an admin template, and then take in an options object where we will pass in the title like we've done previously. The title can simply be Admin, and then add a semicolon at the end. Now for the admin.book file to display this. Let's go and create this admin template, open up the sidebar, into our views folder, create a new file called admin with the dot pug extension. This admin route is going to be fairly simple. At the moment all we're going to do is pass in the title and then create a unordered list. This is going to have some links for the admin to use, such as the links to our new hotel, to edit or remove hotels, and to see any bookings which has been made. Let's extend our layout. Extends layout at the very top, and then replace the content block. Indent it one level we can pass in our h2, which is the title. Then want to separate this title with the links with the horizontal line. These three links are going to be an unordered list. The first list item, nested inside we can add a a for a link with the href equal to forward slash admin, and then forward slash add. This is going to be the route which is going to handle the add in new hotels to the database. The texts of Add new hotel. Then we'll do this two more times. Our second list item is also a link. This href is going to go to forward slash admin and then forward slash edit dash remove. The text of Edit forward slash Remove Hotel. We'll come back to this one later on when we look at editing and removing hotels. The third one again is going to be for later too and this is going to be a link which is going to link to a route which is forward slash admin forward slash orders. The text of View bookings and give that a save. You can test if it's working by going over to the browser and then checking out the forward slash admin routes, and then hit and enter. If we scroll down, we have our title of Admin, which is passed to the templates and then our three links which we just created. If we go ahead and click on "Add new hotel" at the top, we're taken to forward slash admin forward slash add, which was set up just here. This results in an error down at the bottom because we also need to set up this route too, along with a page template. Back over to our router file, which is index.js. Let's set this up with router.get. This router was forward slash admin, forward slash add, to add a new hotel, and then setup our hotelController, and the function name of createHotelGet with a semicolon at the end. You may be wondering why we've called this createHotelGet, rather than simply create hotel. I've added Get onto the end because this is a GET request. Later will also be creating a POST request to this create hotel route. It makes it more clear when we get to that stage. This is now our route. We can now move on to the controller to render our view. Let's click on "hotelController.js" down at the bottom. Let's set this up. It was exports.createHotelGet sets up our function with the request and response objects. This is going to simply render a view. We do this with res.render, just like we did with the adminPage. The template we're going to create is going to be add underscore hotel, and then pass in the objects, which sends the title of Add new hotel and a semicolon at the end. Just like we this admin two we've not yet created this add hotel page. Go to the sidebar, go to the views, and create a new file, called add underscore hotel with the dot plug extension. Now it's a case of making this a form, which is going to be used to submit the hotel to our database. This needs to extend our layout, select all of the files, and then block content. Just before we get to adding our form, I'm going to add a link at the top of the page. This link is going to link back to the admin section, so we can quickly switch between the add new hotel and then go back to our three main links. The link with the href equals forward slash admin. This is going to be a button. We can add a button type. So type equals button. Then we'll also add a class which we are going to be using later when we add some CSS or button underscore small. The text goes back to admin as this is where this is linking to. Then at the very bottom we pass in our page title. To keep this consistent we'll add this in a h2. So h2 equals title. Give this a save and check if it's working. Refresh forward slash admin, forward slash add. Underneath the layer so we can now see our button of back to admin, click on this and then we're taken back to our three links. We can now go back to add new hotel. We need create our form now which is going to have all the fields we need for our hotel. These fields need so much of the data inside our model. Let's go ahead and create our form underneath the h2. The form and then the attributes inside the brackets, the action. This is going to link to the same page, so we can keep the routes with an empty string. The method, this is going to be a post request. Then indent it in one level. I'm going to create a div which is going to have the class of form underscore input. Each one of these form groups is going to have the same class. We can keep the styling consistent when we get to the CSS later. The first one will be labeled and this is going to be for hotel name. So hotel underscore name, and then the text of Hotel name. After the label will then add our input. This input is going to have the type of text, the name of hotel_name. Like I mentioned before, these fields need to match the data in our model. If we go over to our hotel.js and we'll begin right at the top with hotel name. This needs much name, which we gave this in our schema. Hotel_name. Then we'll do the same for description and also for the rest of the fields inside here. Back to our form. The name of hotel name and this field is also required. The next one is going to be for the description. The surrounding div of form input, the label. This one is going to be for hotel description. Then it's x-naught hotel description. This time rather than an input, this is going to be a text area. We do this input, just like the rest of the elements. We can add the name of texts area, and then we add the brackets or the attributes. The name of hotel_description. The ID, which is also hotel_description and let's make this smaller so it all fits on one line with the text area. You can also set the default number of columns and also rows. So I'm going to set the columns to be 13 and also the rows to be ten. This is also required too, like all the rest of the fields. Below this thing,next one is going to be for the hotel image. So I'm going to go ahead and add the wrapper which is form inputs, the label for the image, the textile photo image. This time it's going to be an input for the file. So the inputs with the type of file, since we'll be using uploaded images from the user machine. So the type of file, the name is going to be image along with the ID. Let's go ahead and copy this hotel name. Then after the image paces in, this one is going to be for the star rating. So the label will have the name of star_rating and this will be a star rating of one to five. Star rating for the name is input type is going to be of number. To restrict this from one to five with the name of star_rating. This is also required to. Let's Keep this between one and five. We can also add the min attributes of one and also the maximum attributes of five. Go to under the star rating, we can add this in again, make sure the indentation is correct. This one after the star rating is going to be for the country. So the name of country, the same for the text. The input type is also going to be text too. So we can name this the name of country and also let's give this an ID of country too. This is also required too. The same again after the country. This one is going to be for the cost per night. So the label is going to be for cost_per_night. Text of cost per night to, the inputs is going to be for price. So we can set this to be a number. The name can also match this, so we can copy this and paste this in for the inputs. So we have the type, the name, and we also need the ID of cost per night too. So add this in and the next one after this, if we go right down to the very bottom, is going to be for the availability. So we're going to add some radio buttons. Which we can select if the hotel is available or unavailable. So the wrapper of form inputs is going to have two radio buttons. The first one can have a label and this is for available, the text is available too. Then our input, which is the type of radio. After the type we can add the ID and this one is going to be the same. This also needs a name. This is the time of available. This is the data which we sent to the server. The value is going to be equal to true and we'll set the next one to be equal to false. So if this one is checked, we'll get the available equal to true, and the next one will be available equal to false. By default, we want this true to be checked. So we can also add the checked attribute inside there. Then we just need to do the same for unavailable so we can copy these two lines and then add them below. This is unavailable as is the text and then the input. We can add on, save the name, the value of false. We can also remove this checked attribute because we only want one checked at once. So now the last thing we need to do is a button which is going to go ahead and submit this form. This can have the same form input wrapper. Skip the styling consistent. This time this is a button with the type of submit, the class, the class we've already used before of button_small. This will keep the CSS more consistent and then in capital we're also going to add the button text of Confirm. We can now save this and then go over to our admin. Make sure we're on forward/admin,forward /ads. Now we should see the add new hotel form down at the bottom. If you've seen your form with no errors, congratulations. If you do see any errors, make sure you check over your code for any typos. Before moving on to the next video, we will finally get a hotel push to our database
24. Pushing to the database: We all now set to get to work pushing the hotel data to our Mongo database. In the last video, we created a template called Art_hotel. This form is setup to make a post request. If you go to the top, we can see the method is set to post. This will post our form data so we can go ahead and use it in our controller. Also, the action is set to an empty string, which we can see just here. This will post the form data to the current routes, which is forward/admin, forward/add. If we go over to our routes and enter the index.js, we already have a get request on this forward/add route just here to display our at new hotel form. So let's duplicate this line to create a post request. So copy this and paste this below. We need this to be a post request as we're dealing with a post request from our form. So router.post, again we can keep this one as forward/admin, forward/add because this is the file path which we're going to be posting to, since we've not set our y's in our action. So since this is a post request, we can change create hotel post, and this create hotel post middle-ware will handle what to do when we make a post request to this route, i.e. when we submit the form. Let's set this up in the hotel controller.js under createHotelGet. We can do the same for createHotelPost. So createHotelPost sets up our function with the request and response. The first thing I'm going to do inside here is to see what data we're working with. We can see what data is being sent by the form by using res.json, so I'll put the data as JSON, then pass in the request a body, and this is where the data is stored in our quest objects. So inside of our function at res.json and the form data is stored in the body of the request objects so pass in request.body gives us a save and then over to the browser, get that reload. Now we can fill in some form data to submit. That's just adding a test. That's a test data, choose an image, into our Travel project, and then the public folder, images, and we can choose any hotel image. So I'm going to select Hotel 1, a star rating of three, country, Portugal and any price is fine. We confirm, so as soon as we hit Confirm, we then make a post request from this form. This post request is going to forward/Admin, forward/add. We set this up inside of our index.js to handle this post request to this route. This is then triggering our hotel controller, which is then returning our JSON since we are doing res.json and then passed in the request.body, which stores the data being sent by the form. So now we can see the JSON version of our hotel which we just added. We can see this data is setup in the same format as our model. This gives us a better idea of the data we're now working with. This form has been set up to be the same as our model so the data is in the correct format. Now we know we have an object for our hotel stored in request.body. Will we use this data in our model to push to the database? So we need to require our hotel model first, the top level file. So back to the hotel controller, and at the very top, we can set up a constant called Hotel with capital H, and this will require the models. So as a string when it's passing the file path, so let's go up to our model's folder, and then the name of the model was Hotel. We can then use this Hotel model down in createHotelPost. So we're going to set up a new Hotel, pass in the data from request.body, which we've seen is the object which contains all the fields from our form. They install is inside of a variable or a constant. So const hotel equals our new Hotel. So now we have our hotel and we can go ahead and call safe. But one thing first, I'm going to mark this function as async. Just after the equals, we can mark this function as an async function. This is something which is new in ES 2017 called async await. It really does make our lives a lot easier when working with asynchronous code. What it basically allows us to do is to pause a function until a line of code has finished running. First of all, we mark the function async, just like we did there, and then the next task our function has, is to call save to say this to our database. We do this with our hotel constant and then call.save. With the save, however, we want to make sure the hotel has finished saving before we can start doing things with it. To do this, we can add a wait before our hotel.save, adding a wait before this line. We'll make sure this code pauses and then wait for this to finish before moving on to the next line. The reason why we want to make sure we await the hotel saving before moving on is because we will assume new this hotel data immediately after the save. So we want to make sure that the save has completed, it's available before calling any more lines of code which need this hotel data. So if we give this to save and then over to the browser, gives this a reload and resubmit the form, and then go back over to mLab and login to database. So let's add in our user details. Once our database loads up, we can now see on Java collections we have the hotel collection, which now has one documents. If we click on next to expand, we're then taken to our test, which is the data which we just submitted. This is a big step because we've now saved our hotel to the database. But what if there is an error when saving the hotel? We need to add something inside of our controller to handle any errors. First, we can wrap the code inside the try block. So back to createHotelPost, we can add try and then move these two lines inside of this try block. This will try to run the code inside and test for any errors. If there are any errors, we can handle them with a catch statements. We do this at the end, we catch, we pass in the error, and then inside here we call next, which also takes in the error. Calling next, I'm passing in the era, will pass the error along the middle-ware chain until it reaches an error handler which can deal with it correctly. Remember, we already have error handlers setup with the express framework inside of our app.js, which is down at the bottom here. So back to our controller for this next to work correctly, we also need to pass in next after the request and response objects. So passing next here since we're now using it inside of our function. If we head over to mLab, inside the browser, we can look at our hotel. If we click on this corner and then drag this down. If we notice, inside of this hotel which we just added, there's been a unique ID added with the field of underscore ID, which is just here. Remember from before, we said we wanted to use await to wait on the hotel saving this database before moving on. Making sure the hotel has successfully saved means we now have this ID available before move on to the next line of code. This only because I now want to redirect to the hotel once it's been saved. We can do this inside of our controller with res.redirect. Then passing the file path we want to go to, I'm going to use the back ticks since we're going to add some dynamic data. So forward/all, and then forward/. We can pass in the $ symbol and our curly braces. This is something we looked at early on in the JavaScript section. So inside here we can pass in a variable. So we can access our hotel object, which is just here, and then the field which is underscore ID, and this is the idea which had been added inside of our database just here. Add a semicolon at the end. Now let's check this is working okay by adding a new hotel. So let's go to forward/Admin forward/add, reload the page. Let's say test 2. Test 2 for the description, any image is five here. Then click Confirm. We're now redirected to our route, which we set just here, which will be foward/all, and then the hotel ID. We can now see this in the URL bar at the top. We have our unique ID, which is now pulled in from the database because we waited on this hotel to be created first before moving on to the redirect. If we scroll down, we see an error of Not Found. This is fine because we know we have not created this route yet, but the main thing is we have this unique ID now any in the URL. We'll come back to this later when we create a template to display the full hotel detail. This is a big step forward now, we saved into the database. So congratulations, if you now have this working. If you don't, make sure to check over your code and take a look at the finished code provided or ask in the Q&A sections before moving on to the next video.
25. Querying the database: Before we look at how to get our data from the database or from a query, we first need to add some more data to get back. If you go over to mLab and if you have any this test data coming in place, we can remove it with the bin icon on the right-hand side. I want to clear all of the records in our database and make sure they're all gone. Let's go ahead and create five new hotels to work with. We can do this if we go to admin, so vote/admin/vote/add and then go down to [inaudible] at the bottom. The first one I want to call this Hotel 1 and of course with a more creative name if you prefer. I'm going to grab some [inaudible] Ipsum texts to add for the description. Let's go down and create two paragraphs. It generates, Copy the sample text and Paste it in to our text area. The hotel image for Hotel 1 then select this. The star rating can be four, country Jamaica. Cost per night, now we can leave this as available. Now we redirect it to the hotel. We need to go back to our admin/add. Create Hotel 2. Paste in the description the image for Hotel 2. Then a star rating will go for five, country of Dominican Republic. Costs per night, add something there and confirm. It should be two hotels now in our database if we hit Reload, and there we are. Let's add three more. Again, /admin/adds. We can add hotel number three, adds the description hotel3.jpg. A star rating we go for three this time and Netherlands. Cost per night, say 45 and then confirm. Then we can add Hotel 4, admin adds. Then down to the bottom Hotel 4 Paste in the description the image. The star rating will go for five this time and the country of the Maldives. Cost per night, let's go for 89. Let me some how make this unavailable. It looks like we had a little issue there actually, we have both of these checked. Let's go to our form, add hotel and down to the check boxes or the radio buttons. Sorry. These needs have the same name, them too much in the same group, let's try reload in. Yet now we can just select one of these. Let's go over to the database. Now, we have all these available as true. I'm just going to go into any of these and click on the edit button. We'll set the availability this time to be false. It's Save and go back. Because now I have hotel number three to be set to false. Let's add one more which is Hotel 5. The description Hotel 5 image. The star rating let go for two this time. The country of Greece, cost per night and we can keep this one as available. Now, over in mLab we should now have five records with one of these being unavailable, which are said to be Hotel number three. We'll add the rest of the hotel's later on. But now we have some data to work with. I'm going to show you how to get it into our application inside of the hotelController.js. Let's select this and then we can go back to our list all hotels function. If we scroll up, we have the list all hotels just here. Returning all the data in our database is fairly easy using Mongos find method. Before we render all hotels, let's create a constant called all hotels and I'm going to set this to be await because we want this data to be pulled in before rendering template. We want to select our hotel model and then use the dot find method and a semicolon at the end. The find method will find all documents in our hotel collection and also search the hotel collection because we're using the hotel model just here. Here since we're using await once again, we also need to mark this function as a sync for it to work. Add sync just before our function parameters, we also want to handle any errors again two so we can wrap this code in a try block. Let's cut all these two lines of code, add a try block, paste this back-in, followed by a catch block to handle any errors. This catch block also takes in the errors as an argument and then we can also pass these on to next. Since we now call next, we also need to pass this into the function. Our next as the third arguments and before moving on we should check this is working. We can comment out this res.render that we have above and instead replace it with a res.json. This will add the data to the screen in JSON format. The data want to output is all hotels, which is this constant just here. So save this. Then we need to go over to the browser, and then go to /all hit, "Enter" and now we see all of our hotels in JSON format. This is all the data which we now pull in from the database. This looks like all of our Hotel is being returned. We can see all of our fields including the generated ID. Now, we know this is working we can then pass this data to our page template to go ahead and render. Back over to the controller. Let's comment out the res.json and uncomment out this render method. Along with rendering it, the all hotels templates and passing along the title, we also want to pass in this hotel data of all hotels. Separated by a comma, we can also pass in all hotels and this will now be available to use in the templates. If you give this a save and then go over to the all hotels templates, which is in views. Let's open this up. Let's test this working by selecting p elements and send this to the value of all hotels. Give that a save. Then over to the browser, we can reload the /all routes. Okay and scroll down. We see one giant object on the screen now. This is all the data which is now pulled in from the database. We can see if we look closely, we have hotel one, and then if we go further across, we have hotel two just here, and then hotel three further down. This is all the information from our hotels collection. We can also filter this down if we just want the first hotel, for example. We could select it by the index number. All hotels, the index number of zero, reload. That's all the data from our first hotel. Even further, we can narrow this down to the hotel name. Remember, hotel name is stored in hotel underscore name, so we can access it using the dot notation, so hotel name. There we go. There's our hotel one name. Now we have access to all this hotel data. We need to live through all hotels and display each one in this hotel mix-in. We already have the hotel mix-in just here. Once you create a loop which displays this mix-in with all the hotel information. Delete this p element from here. We can add our level two heading, which is the title. Then after this, we want to create our loop, and we'll do this with each hotel in all hotels, and then indent both these lines in. All hotels is the data which will be passed to the template with all the information from hotel one through to five. Each individual hotel will be stored in this hotel variable. Now if we save this and then reload the forward slash all routes and then scroll down, we see we have hotel number one. If we keep going down, we should have hotel one for each one of the items in our database. This is now repeated the same hotel in our mix-in for each item. This is a step in the right direction because we now have five items on the screen, but we want each hotel to be different. To do this, we need a way of passing the unique hotel data into the mix-in. Remember we said, before that this hotel variable here holds the individual hotel information, so this is the information which we need to pass to the mix-in. We can do this with our mix-in just here and pass in the hotel. This works because mix-ins are compiled to functions which can take in arguments. If we save this and then go over to our hotel data in our mix-in, down to mix-ins then underscore hotel up to our mix-in name at the top. We can also receive this hotel data, so passing hotel as an argument, and now we have the individual hotel data inside of this mix-in. This is where the good part begins to happen. Let's begin by displaying the hotel name. Scroll down to our level three heading. Here we have some hard-coded text because this is a variable we add to the equals, access the hotel, and then dot hotel underscore name. If we save this and then reload the browser, back up to the top, we have hotel one, hotel two, hotel three, four, and five. Now we see all the hotel names are unique. We can now carry on with the rest of the details making them all dynamic. Next, we can add the data from the two links. The href just up here, let's change this to be back ticks because this will be dynamic. This is going to link to the full hotel view, which is going to be our forward slash all. Then we can add our dynamic section, which is hotel._id. We'll deal with this route later on. We'll also do the same for the link below. Copy this and paste it in for the second link. Having these two links means both the image and also the hotel name will link to the full view of the hotel when the user clicks on either of these. Then we can deal with the file path for the image. At the moment, we just have the image hotel one hard-coded inside here. Let's change this to be back ticks forward slash images, the hotel's folder inside the images, and then forward slash we can open up the curly braces and then add hotel dot image. Inside of our database for the image, these images are saved. If we take a look, for example, image here is saved as hotel2.jpg. If we go over to Visual Studio and then into public and then images inside of the hotels, this name will match the name of the images which we'll have in here. Now if we save this and then reload the browser, we should now see the unique image for each hotel. We'll come back to the images later because we're going to be using Cloud Storage for image uploads rather than storing in our own project. Now let's head back over and finish off the rest of this dynamic data. The p elements are the bottom, but also now going to be dynamic. We need to surround this in back ticks, change the star. Rather than having the hard-coded value of four, we can again pass in our dynamic data. Hotel and then the field name, which is star underscore rating. The same for our country. We can surround this in back ticks, replace the hard-coded country with hotel dot country. The cost per night. First of all, we can add a currency symbol and then a space to create our dynamic data. Hotel.cost_per_night. Let's head over to the browser and see how this looks. Refresh our all hotels routes and check these all difference. We have four, Jamaica, and 67. Then we have the Dominican, Netherlands, and also the Maldives with different star ratings and prices too. Excellent. You should now be seeing all of your hotel data from the database. You may be also wondering where we have the hotel description, well we'll add this back in later because this is only to be displayed on the full hotel detail view where we have more available space. There is one little problem here though. The problem being we can also see hotel three. If we scroll down to hotel three just here, remember we set this hotel three to be unavailable when we first created it. This is pretty easy to resolve. All we need to do rather than just using the find method, we can also pass in a query. Back over to the hotel controller and to list all hotels. Inside of this find method we have here, we can begin by passing in a object, and then we can select the available fields from our database. Now we only want to find hotels which has this field set to true. We can do this with the $ symbol eq and set this to true. $ sign eq is a MongoDB query operator which checks for equality. Basically, only hotels with the field of available will then be returned. Now if we save this and then reload, now hotel four, and then it jumps to hotel two. It looks as though now hotel three is not being pulled in from the database because it doesn't match our query. Good. This is now all working. We're going to continue pulling in data from our database in the next video where we will be getting our hotels based on the country.
26. Distinct values: If we head over to mongodb.com, which is a homepage for our database. Up at the top, we can see a link to the documentation. This documentation is going to give us all the information we need to query our database. On the left-hand side, if we click on getting started. Here, we can find a full reference to all the database commands, which we're use in this course, along with many others. Down at the bottom we have a reference section. Click on this, and then go to database commands. This will give us a list of different methods which we have available. Let's scroll down to the use commands. Underneath here we have a find method. If we scroll down to the query and write operation commands, you have this fine method which we used in the last video to get all of our hotels. The next one which will be covering in this course is distinct and this command is near the top. We have this just here. For this project, we need to get a list of available countries which have hotels located in. We may have multiple hotels with the same query law. For example, we could have six hotels located in Mexico and we don't want the word Mexico to a appear six times in our country's list. Distinct will allow us to return an array of only the distinctive countries, meaning the only Mexico will appear once from our example. Let's begin by creating the all countries templates for this page. Open up the sidebar, let's close off some of these. I close this down. If we go to the views, we can create a new file inside of here called, all_countries with the.pug extension. We go ahead and add our basic code; extends layout, and then the block content, the h2 of title. Then next stop, we can add the route to the index of JS file. Go to our index inside the routes folder, open this up, and then after the " /all route", we're going to add router.get. This you got to handle the /countries routes, the hotelcontroller, and this time we're going to set up a function called listAllcountries, add the semicolon at the end. We have not yet created lists all countries. So, head over to the hotel controller file and then we can add it in. Let's open this up. After a list all hotels let's add exports.listAllcountries. Setup our function. I'm going to mark this as async, because we're going to need an await inside of here. We can pass in our request, our response, and also next. Then add our try block, try and run the code, followed by our catch, which is going to take in any errors and then pass them over to next. Make my error with no s. The code inside of the try block will be fairly similar to the all hotels above. We begin by creating a constant to store our data in. I'm going to call this constant allcountries. This time now we select our hotel model, so set this equal to Hotel capital H. Instead of the find method that we used before, this time we use.distinct then to return an array of distinct countries, we can pass in the country field as a string, and then under this we can render our templates with res.render. The template we want to add is the one that we just created of all underscore countries. Separated by comma we can pass in our title. Way to the text of, browse by country, I make this little bit smaller, then we can pass in our all countries data, which we set up here. Make sure that this is available to use inside of our template. Once this is done, hit save and then go to the browser, open up our projects, then we can go to /countries, and then scroll down, and we'll see the page title of browse by country. We only see this page title, because this is all we have currently setup in the template. Let's go to all countries.pug, and fix this by adding the all countries data. Which we now pass in to the template. I'm going to begin with a wrap if, just like we did with the hotels. This is going to be called country wrapper, then create an unordered list to display the list of all countries. To get all of these countries, we need to loop through this all countries data, which will pass to this template. We can do this with a loop, so each country in all countries, create a list item, which is also going to be a link. Pass in the href. This is going to be equal to our back takes, and then /countries/, then we can add our dynamic country name. So these are the country inside here, which is this individual variable that we loop through. We can then output our country name. And we can make this dynamic using the # and then the {}, and then output the country. If no countries are available inside the database, we can then follow up with an else block. Just on the same level as each, we can add else, which I'll put a list item with the text of, there are no countries. Now if we save this file and then over to our Projects and then reload our forward/countries routes. Then scroll down. We don't quite see the information which we're expecting. We were expecting a list of countries. Again, a lot of strange looking code down below. The reason this is happening is because we are not awaiting our data to come back before we're trying to render this to the screen. This is one of the problems we mentioned before, because we're using a single weight, we need to await our data to come back from the database before we try to use it in our application. If we go back over to the hotel controller and the list all countries, we have this function marked as a sink, but we're not awaiting the hotel.distinct value to be returned back from the database before we pass it to our templates. Hopefully now if we save this and then reload, we now get list of countries being displayed. If we click on one of these, we're then it taken to our route of countries, forward slash and then Jamaica. We have an error down below because we're not handled this route just yet. Let's try another one for the Netherlands. These all appear to be working. It would also look nice too, along with this country name if we can also put in a image related to this country. We already have the country images, which is stored inside of our Images folder. Public images. Then we have some countries with images down below. Let's go ahead and make use of these images in our templates. So just under our link, go on to the next line. Then we can set a image indentity in one level. This also links to the same route above. If the image needs a source, is it going to be equal to o back ticks, the forward/images folder, the country's folder. Then we can add in the country name, which is simply country. Then we need to add the.jpg extension onto the end. If we now save this and then go over to our forward/countries routes, reload. It looks like we're typing errors. So let's take look at this. It's just because we've added a semicolon in our templates, which we don't use in pug. Refresh. There's our country images next to the name. At the moment, this looks like they are now all working but at the moment though we can't be too sure. This is because we only have one hotel in each of these countries. So we don't know if it's showing a distinct value just yet. We can test this by adding small hotels to our database. I know it's a bit repetitive, but let's now go ahead and add the remainder of our hotels. Then we can use this for the rest of the project. Let's go to forward/admin, forward/add, down to the bottom, I'm going to add hotel number 6 and then crop the Lorem Ipsum. Go to lipsum.com, I'm going to create two paragraphs and then hit "Generate". Copy this and paste in our description. Hotel 6 has the image, the star rates in this time of four, the country, Sri Lanka, the costs, 57. This cone can be available too. Admin/add, Hotel 7. Add the description and image. The star rating of five, the country of USA, 78. Keep this as available. Back to our admin routes. Create hotel 8. I'm going to go up to hotel 12 to much all images which have been provided with the course. So add this in and hotel 8, the star rating, the country of Maldives and the availability as true. Now we can see we already have more than one hotel in the Maldives. Back to forward/admin, forward/add. Hotel 9. Based on a description. This one can be a star rating of four. The country of Mexico. Cost per night. Confirm. We need it now, let's go on to the admin adds and creates number 10. [inaudible] four, hotel number 10, the description and also the image which is number 10 too. Open this up, the star rating of three. Let's again go for Mexico. Have a price and then hit "Confirm". Admin adds, hotel 11. The star rating, let's go for four, the country of Thailand, cost per night, 39. Confirm. Finally, we can add the hotel number 12, which is the last one. Sounds at the bottom. Hotel 12. The star rating of three, Dominican Republic, 56 and hit "Confirm". Great. Now over two mLab we can now refresh. I know this is little bit boring and repetitive. But now we have all 12 hotels now in the database. So we have plenty of information to now work with in our projects. So I've provided 12 images. You can of course add more hotels, if you prefer. There is now more than one hotel located in the Maldives, Mexico, and also the Dominican Republic. Now if we go over to forward/countries and then hit "Enter", scroll down. We can now see all of the extra countries which we've added. There is only one of each volume. So there is only one Mexico, one Dominican Republic, and also one Maldives, which means that with distinct values are now pulling in correctly. Now we're going to move on to looking at the aggregation pipelines.
27. The aggregation pipeline: The aggregation pipeline is an interesting feature of MongoDB. It basically allows us to process data one stage at a time. Currently, only Mongo documentation from before. If we go to the menu on the left, we can go up to the aggregation section, click on this. If we scroll down, we see a diagram which has an example of how we can use the aggregation pipeline in our projects. This example uses a collection called orders, which we can see here. Its comparable to our hotels selection, which we have in our database. This image shows free stages of the pipeline which results in the data we eventually want over on the right-hand side. Each stage in the operation could do things such as filtering, grouping, or sorting documents until we end up with the correct data. The first stage in this example here shows four records inside of our collection. We then sets a matching stage, we then declare that we only want to match any documents with the stars code of A. Over on the left-hand side, we see the only freedom of this A code, and the last one has D. Therefore, only three of these four documents can match, then these three move on to the next stage, which is in the middle of this diagram. The stages are grouped together by the customer ID, which we set here with the group stage, where we're grouping with an ID field. These two have the same Customer ID. Therefore, these are grouped into the same results. Then the third one is unique too. We now filter down to two results. The second part of this stage is to group together the total amount of these two orders the same customer has placed. This is what we see in the total of the final stage. To find out more about what we can do for each of these stages, we can click on the sidebar and go down to aggregation reference, then aggregation pipeline quick reference, then scroll down. Here, we see a list of all the stages which we can use. We've already seen some of these in the examples before, such as the group stage. We have group just here, and also scrolling further down. This is the match stage which we've also seen. You can click on any of these listed stages and find out what each one does. Or I'm going to run through some of these examples now, inside of our projects. If we go over to the hotel controller.js file, we have functions to get all of the hotels and all of the countries in the database. Which of these two just here, but what if we want to be a little bit more specific about the data which we got back on the homepage. I also want to show these hotels both limit the results to be just nine so we don't have an overcrowded homepage. Once our database stores a lot more different records. Also the same for the countries too. For these we can use the aggregation pipeline to filter down these results for the homepage. Let's create some filters to filter down these values. Exports.home, I'm going to call this function the homepage filters. We set this up as an async function so we can use await, pass in the request, the response, and also next. Then a try-catch block to handle any errors, passing the error. Then call next with this error. Let's first deal with filtering the hotels inside of the try block at the top. We can use the aggregation method. First of all, let's set up a constant to hold these hotels in. Const hotels equals awaits our hotel model. Then we call a method on our hotel model, just like we did above with distinct and also for the find method. But this time now we use hotel.aggregates with a semicolon at the end. This takes in an array of the various stages. Add an empty array inside here. The first stage I'm going to use is the match stage. Open up the curly braces, $ symbol match. Then we want to match the hotels which are available. Set the available fields to be true, then add a comma the end. Next step, so the homepage it doesn't get too crowded. The $ sign sample stage. Will randomly select the number of documents which we specified. $ sign sample, we can set this sample size to only return nine documents. You can of course change this to be any value which you prefer. This aggregation which we setup will return towards nine random hotels, which have their availability set to two. We can now do a similar thing with the countries. We also only want to show nine random countries on the homepage too. Just like earlier when we were dealing with countries, this needs to again, only return each country once. Even if there's more than one hotel per country. To do this, we have the group stage. Let's set up a number constant this time called countries, set this to be hotel. aggregate, passing our array. The first one is going to be the group stage, $ symbol group. When we're using the group stage, we also need to pass in an ID with -id is equal to output our documents with an ID field which contains the distinct group by a key. This field is mandatory and we're going to group this by a key name of $ symbol country. This will group all of our hotels by their country and then separated by a comma. We can again setup our sample, the sample, just like the hotels is going to return the sample size of nine countries. The group stage will take in all countries as an input, then I'll put one distinct value. For example, if there are two hotels in the USA, we only get the value of USA ones inside of our array. Now we've created our filters. We want this data to be displayed on the homepage. Let's go and start this over in the index.js. Yes. We can change the controller up at the top. So for the forward slash home route, instead of using the hotel controller to homepage. We can change this to be the homepage filters, which we'll setup, back over in the hotel controller. If we now scroll up and find this export.homepage, we don't need this anymore, so we can comment this out. Finally down in our filters, down at the bottom, the final stage of this try block is to do a res.render. We want to render our index page, which is the homepage. Then inside here we're going to pass in our two values, which is the hotels and the countries. We can loop through these on the homepage, parsing the countries and then now hotels and actually we also need to await of countries too. So as a side note, awaiting to promises like this, using await here and also await here is not a great idea. This is something which we'll come back to you and fix later on. For now though, over in our index or poke file, we can deal with these countries and also the hotels. So for now though over in our index.pug file, we can now deal with these countries and hotels data. So go to views and then index.pug. Let's close the sidebar. At the moment if we go to our home route. So click on the logo at the top. On this homepage, we have an error because we're not yet parsing any country detail to this hotel mix in. Let's now go ahead and loop through all of the data which will now be in past and then we can display this hotel mix-in for each one. After our text of CO, create on another list, we can then loop through all hotels with each hotel in hotels. Remember hotels is the data which has been passed to this template just here. So each hotel in hotels. For each one in the database, we also want to display this mix in and as well as displaying this, we also need to pass in the individual hotel data, so this is available in the mix in. Let's give this a save and then reload on to the homepage. Now we can see some hotels now listed on the screen. Let's just check. We have nine. We have 1, 2, 3, 4, 5, 6, 7, 8, 9. That all seems to be working okay. Back over to the index. We need to do a similar thing now for the countries. Underneath here we can add a wrapper. Make sure this is at the same level as the hotel wrapper above at.country_wrapper, for the CSS later. The text h2 of countries, a link, and this is just like the link from above here, which goes half the href to forward slash countries and then inside the brackets the text of see all. We have these two links on here for the countries and also for the hotels. Because remember we limited the sample size and the homepage to only show nine hotels on nine countries. Therefore, link in to a new page for all hotels and also all countries will then allow the user to see everything which is in our database. But we'll come back to this later on. Then I will unorder list to display the countries, will do the same as above. We'll say each country in countries and then create a list item for each. With a link nested inside. The href is with dynamic so open up the back takes to go on to link to forward slash countries. Inside here we want to pass in the country._id. We need to use country._ id because if you remember from before in the aggregation method, if we go back to our controller, we set the unique ID in the group stage to be the country. Now inside of our index, this ID field is where our country name is now stored. Then we can display the same value. So the harsh, the curly braces and then country._ id to display this as text next to the link. The final thing for this template is to also add the image, the image source. Again, this uses the back takes. So forward slash this is the images folder, the country's folder to grab the country image and then just like above, we can pass in the country name, which is the value of country._id with the. JPG extension. So just so this underscore ID is a little bit more clearer, if we go to the hotel controller rather than rendering this index page, let's just do a quick res.json and then we can display the countries data. Save this and then reload the homepage. Now, we see this mandatory ID field is now being set to the individual country. This is why we are using the underscore ID in our template to access the country values, and then display them on the screen. Now, if we reinstate our hotel controller, remove all res.json and add the res.render back in, save this and then reload our homepage. We should now also see nine countries too. Let's go down to the bottom, 1, 2, 3, 4, 5, 6, 7, 8, 9. For hotels, we also say nine, which we counted before and if we scroll through, we shouldn't see any reference to hotel 3 because this one is said to be unavailable. So this all looks fine and each time we refresh, we should see these in a different order because we're using a random selection of our hotels. This is an introduction to the aggregation pipeline. We will return to this later when we begin working with the search facility we added in the header. In the next video though, we'll continue to work with Mongo by looking at how to update data in our database.
28. The edit and remove form: If you go to our application and then go over to the admin section, so /admin and then go down to the bottom, we've already used this add new hotel link, which you see down here. But now we're going to move on and work with the Edits/Remove Hotel link. It is going to link to a form where the admin can search for a hotel by either using the hotel's id or the hotel name. Then once this form is submitted, it will return the match in hotel and allow us to move on to either update or delete the hotel. The first step is to create a page template with a form. Let's go over to the sidebar and then into our views, create a new file called edit_remove.pug. Inside here we can extend the layouts so extends layouts and then block contents and make sure that's spelled correctly. I'm just going to take a simple form where you can search for the hotel id or the hotel name. To begin, I'm just going to add a level 3 heading for the title which we'll pass later, the form, the action can be equal to an empty string because we're going to be adding a post request to the current route which we're on. The method is going to be POST and then nested inside here I'm going to add a div with the class of form_input. Let's begin by adding a label for the first one which is going to be the hotel id, so label for hotel_id and then the text of Hotel id. The input, it's going to have the type of text, the name of hotel_id, and then an id which matches its label. Remember before we said that we can either search for this hotel that wants to update or delete by using this hotel id, or we can search for the hotel name if we prefer. Let's add a p elements with the text of or so the user knows they can either uses it's id or the name. I'm going to copy this form inputs and then paste this in below. Make sure that it's all lined up nicely and then this one's going to be for the hotel name. Change id to be name, the name too, and also the id. This also has the input type of text too and the final thing we need to do is add a new form input. This is going to be for the button to submit which is going to be type of submit, and also the class to match some of the other buttons which we created earlier of button_small. The texts of confirm. Save that and then we're good to go. This is a form which is going to search for the hotel which you want to update or delete. If we now go over to our admin and.pug file, this is the middle link which looked at before. This now links to /admin/edit-remove so I'm going to copy this and then we can go ahead and deal with the route that this is linking to. We do this as always inside of the index.js file. I'm going to add this inside of this admin section. Router, and this is a get request, pasting the file path of edit remove. This is going to run the hotelController and I'm going to create a function called editRemoveGet. Now we can create this editRemoveGet inside of the hotelController, so down at the bottom, exports.editRemoveGet and set this up to be a function which takes in a request and response objects. All we need to do inside here is to arrest or render for all page template which is created. The page template was called edits_remove separated by comma. We can then pass in our title for our page of Search for hotel to edit or remove. Add a semicolon at the end. Save this and go over to the browser and then click on Edit/Remove Hotel. Hopefully we should get our form down at the bottom. Now we have our form once the admin hits the confirm button after adding an id or a name. This will then send a post request therefore we need to solve this post request again in the index.js. Then duplicate the code from before, paste this in. We are going to be using the same route but this time creating a post request. router.post and instead of editRemoveGet, it's going to be editRemovePost. Then set this up inside of our controller, also controller.js, so exports.editRemovePost. This is going to be dealing with the database so we can mark this as async so we can use a wait inside the function, pass in a request, response, and also next. Set up our try block and also the catch for any errors which would then pass to next. The first thing we're going to do in side of this try block is to grab the data which is being sent to us. Inside of our form, we're going to either receive a hotel id or the hotel name so let's have some constants to store these values. The first one of hotelId. This hotelId is going to be stored inside of the request.body. Because we're making a post request, we can access this data if we go over to our form in Edit/Remove. Now we can access this with the name which we gave it inside here of hotel_id. We use this inside here, so hotel_id, and I'm going to use the two pipe operator to say all or null. The reason we're doing this is because only either the hotel id or the hotel name is going to be present. Therefore, the overall is going to be null. Therefore, we need to handle what will happen if we get a null value. We can do the same just below. Constant for hotelName equals req.body, and this one is.hotel_name, so we will either have the hotel name or it will be null. Just like earlier, we're also now going to search or model using the.find method. Let's have the constants called hotelData equal to await Hotel.find. We're going to do something a little different this time because we don't know if we're searching for the hotel id or the hotel name. Because we can't be sure which data is being passed to us, Mongo provides us with the all operator and then we can provide an array of these two values to either search for one or the other. Passing our objects and then use the $or. This is going to take in an array and inside this array we can pass in our two values that we want to search for. Open up the curly braces. The first one is our hotel id. If we receive the hotel id from the form we want to search by the id. The id Mongo is _id so we can check if this is equal to hotel id which is our constant just here. If this isn't present, separated by comma, we can add a second check. The second case is for if we receive the hotel name instead of the id. Therefore, we want to check the field called hotel_name from our database. Then we want to check if this is equal to our hotelName variable, which we have just here. Add hotelName- I hope this makes sense. We are doing a find method, and we're using the o operator to check if one of these fields is a match. After using our dot find method, we're now going to specify a collation to search for. If we just remove the semicolon at the end for now, and then chain onto the end.collation, open up the brackets, and then pass in an object. Collation allows us to make language specific matches, so I'm just going to type itself and now. The locale is going to be sent to en, add a comma, and then the strength as a value of two, so let's add a semicolon at the end. Using collation, like we have here, collation allows us to make language specific matches. Here we're declaring we're using English texts, and the strength is an optional value. Setting the value of two is a secondary level of comparison, meaning it's not case sensitive. This is good because it means we can, for example, just type the word hotel inside of our form down here, just like this, without using a capital h, and you'll still find the hotel from the database. There is also different numbers you can use here too, and these are all listed in the documentation if you ever have a need for them. Then we're going to add an if else statements to handle the outcome of what to do if a result was found in the database. Back over to our code. After this creates an if statement. If the hotelData which is this constant just here,.length is greater than zero. Here we're basically checking if this fine method has stored any values inside the hotelData. If it does, the value will be greater than zero, so we can then go ahead and do a res.json, and output our hotelData. We will of course come back to this after making the hotelData our template, but for now we can just leave this as a res.json, then we add to the return keyword afterwards, so we don't move on to the l statement if the section is true. If it's false, we then create an else statement, and then we could do res.redirect to redirect the user to /admin/edit-remove. Basically if no data is found in the database or there is no matches, we'll just get redirected to the current page, so off to the browser we can give this a go. Then let's test for hotel 7, it confirm, and then we get our res.json with the hotelData returned. The hotel data matches hotel 7 which is good, and we also used the lower case for this search 2. Let's also try this with the hotel ID. Let's grab one of these hotel IDs from mLab, hotel 6, add this in, it confirm. Now we get back the value of hotel 6. What I want to do now, rather than doing a res,json just here, I'm going to create a new page template called hotel on the scroll detail. This will basically be the extended view of the hotel with the full description. Let's begin by removing our res.json, and instead do in res.render, pass in our template which we are going to create of hotel_detail. The title for the page of add/Remove Hotel, and then pass in our hotelData, then open up the sidebar. We can now create this hotel detail templates. Inside of views, create a new file of hotel_detail. I'll put. This templates will also be reused later, we'll also need it when we click on any of the individual hotels in our list to be then taken to the extended description view, so let's get to work on our hotel detail, so extends our layouts. This page is also going to be used to replace this res.json, so as soon as we have a matching hotel, we'll then display this hotel on the screen. We also need to import our hotel Mixin so we can reuse the same code and display this in place of this res.json. In our template, we can include mixins/_hotel, block contents. Indented m we're now going to loop through each hotel in hotelData. HotelData, remember, was passed to this template just here, back to our templates, we can create our loop, each hotel in hotelData. We can use the div of.hotel, so much as the rest of the CSS later, and then other will mixing will plus hotel passing in the individual hotel which we have here in the loop. Now if we save this, and then over to our code, reload the browser, and then scroll down. Now rather than this res.json, we're now rendering our hotel using this hotel detail.profile. We also pass in his hotel to our hotel Mixin to reuse the code, which displays our hotel. We're just about done for this video, the final thing I want to do at the top of this hotel is to add two buttons. One is going to be a button which is going to go ahead update this hotel which we selected, and then the second one is going to be to delete this hotel. In the hotel detail above our Mixin, let's add a link for our button. The href is going to go, I need the abatics, the hotel symbol. Hotel._ id/update. Here we're creating a route of the hotel ID, followed by /update. This is what we use in the next video to handle the update, nested inside here, add our button with the class of button small, and then the text of update hotel. Now if just copy these two lines, and add this in once more just below. These can be also for our delete routes, so change is to delete. Then this one can be Delete Hotel. Give that save and then reload the browser, confirm the form submission, and there's our update and delete buttons at the top. Later on, we'll also hide these buttons so only the admin can see them. Now inside of our admin section, we've created a button down at the very bottom which links to the edit remove hotel view. Inside here we can now search for a hotel by either using the ID or the name of our hotel just like this. We can then click confirm, were then taken to the hotel which was selected, and then we have the option to either update or delete this hotel, and this update hotel functionality is what we're going to be moving onto in the next video.
29. Updating records: We've now successfully searched the database for the hotel and we show it in our view. In the last video, we also added these two buttons to update and also delete the hotel. If we click on the ''Update Hotel'' button, we now pass the hotel ID in the URL as a parameter, just before forward slash updates. This is because we set this up in the hotel detail.profile. We set this up just here inside of our link. Having this unique hotel ID is how we're going to be selecting the hotel from the database we want to update or delete. First, we can handle the route in the index.js file. In our admin section on the edit remove, let's add router.get. In this video, we're going to be handling the updates. The route is forward slash admin forward slash. We want this section to be dynamic so we use a colon and give this ID a name of hotel ID. Then update. We want to run the hotel controller. Then create a function called Update hotel get. This will then display a form on a screen which will allow us to change the hotel details before creating a post request to actually submit these to database. Over in hotel controller, we create this update hotel gets. Let's go to the hotel controller.js. Down at the bottom, exports towards updates. Hotel gets, which is going to be a sink. Smart dysfunction with the a sync keyword, passing new request, the response and also next. We can add a try block. Then add a constant of hotel. This is going to add weight on the data when we go to the database and find one record. The one record we want to find is the hotel which is passed in as the parameter at the top here. Inside of the request object we'd use need.params. Awaits the hotel. Let's find one which will return one record which matches. We can match this with the _ID field. The ID we want to match is stored inside request.params. We named this if we go to index.js hotel ID. Request.params hotel ID. After this we then want to add our cache block just afterwards. To cache any errors pass this on to next for our middle ware, so next error. To check this is all working okay after our constant just here. I'm going to do res.json to see what data is returned back to us. We can then pass in this hotel which we are searching for and then go over to our project and then hit reload. Now, we can see we getting the details back from hotel full which is the ID which is passed in as a parameter in the URL. If we go back to our admin and search for a different hotel in Number 6. I'm going to update. We got the value of hotel 6 returned back to us. Now we know we're getting the correct information instead of this res.json we now want to render a page templates. The template I'm going to reuse is the add hotel templates. This is basically the form which has all the fields which we need for our hotel. We can go ahead and modify any of these and then update. Instead of the res.json changes to be res.render. Passing in our templates of add hotel. The page title of update hotel. Then finally we can also pass the data stored in our constants of hotel. Passes to our templates and give them a safe. Now, we go to the browser and then he Reload instead of the JSON we should now see our page now has a form is at the bottom. This form is going to be used to update the hotel. Inside of this form, something we ideally want to do is to have the data passed into all these fields already. For example, it should say hotel 6. We can go in and just modify the data which is there. We can use these values straightforward just before we do this, I'm going to put this form into a mixin to keep things organized. Over to the sidebar and then side the Views and Mixins, create a new file called underscore hotel underscore form. With the.org extension create our mixin name at the top just like we did before. Hotel form which is going to receive the hotel data as an argument. Then Hotel.org on the sidebar. This is the form which will now rendering for the updates and also the Add Hotel section. Then we can put this down from our form all the way right down to the bottom. Our full form section and save the file. Now go back into a mixin of hotel form and we can paste this in. If we go back up to the top and and then indented correctly, save that file. With mixin now setup we can now go back to add hotel.pug. Then include the mixin at the top of the file. Include the mixins which is a folder and the name of underscore hotel, underscore form. Remember we don't need the PAG extension. We can include this mixin anywhere in our file. I'm going to add my back end at the bottom. We plus hotel form. Person in the hotel data. If we now save this and test, it's working okay. I reload in, we still see the form is now on the screen but we've outsourced this to a mixin. Back to making these form fields, have the hotel data already populated. This can be added inside of the mixin that we just created to the hotel form. Lets go down to one of our fields to begin with. Let's start with the hotel name. The way we can do this to set the volume. Inside the inputs we can set the value equal to a hotel which we receive in as an argument here. Then the name we want is.hotel_name. Actually this is a string. We don't want to use the quotations. Now if we save this and then it reload our form. We now see the value of hotel 6 which is now being passed into our form. We can do the same with the other fields too. The description. Go into our text area because we say text area. We need do this a little differently. We need to set the text to be equal to hotel.hotel_description. Rather than using the volume attributes like we do in other inputs we can go down to the star rating. We did the image so we don't need this for now. The star rating takes you come back to the value of hotel.star_rating. The country. Hotel, country, the cost per night. Again after required value is going to be equal to hotel dot cost per night using underscores. Save this and then check this out in the browser. Reload. Great, this now makes updating the information for a hotel so much easier. Next, we need to set up the post request to actually handle the updates when we click on Confirm button. Pass over to the router, which is inside of the index dot js. Let's close these down, open up the index dot js duplicates over get request for updates. But this time, changes to be post and then the hotel controller is going to be update hotel post. Then create this over in the hotel controller. Exports dot updates hotel post. This is going to be in a sync function with the request and the response objects all the way next for the middleware. Setup our function. We can begin with the error handling. Try and also a catch block, checking in the errors. Then passes to one middleware with next. There we go. Inside of our try block, the first thing I want to do is settle by constant. This is going to be to store the hotel Id which has been passed to us. This hotel Id number is available as a parameter which we have just here. Let set this up now, const hotel Id is equal to request dot params dot hotel Id capital I. Then a second constant of hotel, which is going to be equal to await hotel, which is our model. This time we're going to be using a method called find by Id and update or a common case or find by Id and update. This is a Mongo method which we can use to then pass in the hotel Id, which we have star just here. We can then obtain the record inside Mongo and then return back towards the new details. First of all, the first arguments which we're going to add, this is variable of hotel Id. This first argument is the idea of the record, which we want to find inside of our database, separated by a comma. The second parameter is the data we want to use to update it with. This data can be found inside of the request, the body. We also use requester body when originally created the hotel, which is just above. If will go up to create hotel post, which was a function we originally used to setup a new hotel. Remember inside here we created a new hotel using information from the request object stored inside the body. This is the data which is passed in from the form. We're going to be using the same data down below. But this time, rather than creating a hotel, we'll be using it to update. The third parameter. Finally, I'm going to add a options objects, so separated by comma at an objects and we can set new to be true. By default, after updating, we will still get the original record returned back to us. If we go ahead and set new to be true is we'll make sure we get back the modified or the updated version to show in our app. Then over to the browser. We can now make a change. Let's call this the hotel 66, it confirm. We don't see anything on the screen because we're not setup what's doing next. But instead, if we go over to mLab and then refresh. Now we can see we have the hotel 66 inside of our records. Back to our project. We can see this is still spinning in the corner. Ravenous browse hung and we're going to do a rest or redirect to then redirect this to the route, which won't handle this. After our constant of hotel, let's do a rest dot redirect. Inside the back ticks we can add all slash all and then forward slash the Id of the hotel. We have this stored inside of this variable here. We can paste this in, save this, and then reload. Our post requests is now updated database and also redirected us to forward slash all and then forward slash the Id of the hotel. Down at the bottom, we see we haven't set up this route just yet and this is why we see an error. But the hotel is still updating inside the mLab. We'll handle this route later on. This same route is also needed when we click on Hotel. If you go to the homepage by clicking the logo, and then click on any of these hotels. See the full detail. This takes us to the same route which is forward slash all and then the hotel Id and of course we've got the message of not found because we are not yet created this in the browser. Before we leave this video there, let's try one more updates on a different hotel to make sure everything is working okay. Back to the Admin, so forward slash admin, Edits and Remove. We can go for hotel number 12, Confirm. There's hotel 12, click on the Update button and changes to be hotel 122, Confirm with the mLab. Let's scroll up and down so you can see these reports, our normal hotels there. Go to the next page. There's our updated hotel name just there. I'm quickly going to just reinstate these back to how the were. Hotel 12, Confirm this. Then we can go back to Admin, just forward slash admin inside the URL. We can go also to hotel 66, which is the first one which we edited. We can see we've not added an image for this. Let's go to update changes back to hotel six at the hotel six image. Then update this in the database. This is all our updates now working fine. Before we move on now this one small thing we need to fix only if we go over to the Admin. Then if we go to Edit and Remove, search for a hotel, click Confirm and then to Update. Currently we have all the fields inside here or repopulated and this works really well when updating the hotel. However person in this data will create an issue when we're adding a new hotel. Let's just quickly add the hotel 12 image back in and then Confirm. Then if we go to Admin and then forward slash add. Remember, forward slash adds uses the same templates of the same form as we used to update the hotel. But we can see inside of the template that we have an error. These errors are caused because inside of the form we access in the hotel data. Go to a mixin and here we're accessing hotel, which is passed in a pile of options here. Basically this form is expecting to receive the hotel data. However, if we go to the hotel controller and then if we go to update hotel get. Here, we presented the hotel data which it needs. But if you scroll up to where we created the hotel, which is in create hotel get just here. This is also using the same add hotel form. However, we don't pass in any hotel info, and in fact we have no reason to. One way around this is over in the hotel form mixin. Underscore hotel, underscore form, we can go ahead and initially set hotel to be an empty object. Hotel is equal to an empty object and then save this and reload. Now we can see when we add a new hotel, we now have this form back on the screen. Adding hotel to be in an empty object this way will make the template aware of the hotel name and you will know if there's an error. But since it's an empty object it won't interfere with our fields. Great, the main objective in this video was to successfully update the hotels in our database and we now have this working. Next, we'll be covering another important task too and this is to be able to delete records from our database.
30. Deleting records: The final big action we want to perform is to be able to delete items from our database. You will often hear the acronym CRUD, when dealing with databases. Which is short for the four main actions which are create, read, update, and delete. We've already created data, we can also read data too and then in the last video we updated our data. It's now time to look at deleting records too. Let's go ahead and create a test hotel to work with over an admin folds/add. That's our test, test, and we can choose any image, any star rating is fine and the country and then confirm. After this redirect, we now redirected to formal/all and then the ID of the country which we created. Grab this new hotels ID by copying this section here. Then check this is stored in a database over an mlab, so hit reload. Just double check, we have our test, so go to the first section, and there we are. There's our test hotel inside of the database. If we go back over to our projects and then go to admin so forward/admin, we can select, edit, and remove hotel. Just like we did in the last video, we can paste in hotel ID, which was created for tests, confirm, and then we're taken to the full view and we will have the buttons from the last video where we can update and delete the hotel. Click on the delete button this time and then forwarded to forward/admin, forward/our hotel ID, forwad/delete. This is because we said this earlier in the hotel detailed templates. Open up the sidebar, and then go into hotel_detail. This is the link which we said use inside of here, and this is what we're going to go ahead and handle in the routers index [inaudible] file. Close it down and then over in the index.js, this time we're going to set router.get to be the string winter scene of admin forward/our dynamic segments, which is hotel ID capital I, and then forward/delete. This follows the same pattern as the update section just above but this time using delete. We're then going to create our hotel controller.delete hotel get. Then over to the hotel controller we can now create this function. Hotel controller.js and then down to the bottom, so this is exports.delete hotel guests. This will be a sync function so mark this as a sync. As in our objects or request response and next fall or middle ware setup all function so this is going to be pretty similar to a booth. We also need to ground this hotel ID from the premises, so paste this in. Willing not to say a power hotel constant, so const hotel and this is going to be equal to await, wait of a hotel model and then use the method called find one. This is going to find one record inside of our database and we want to find it by the hotel ID, which we have stored just here. As an object, add this in. We want to find one record by the underscore ID field and the ID we want to match to is hotel ID, which is this variable just here. Then we can go ahead and do a rest or render to display the contents of our templates, which just like before when updating, we're going to render the same ad hotel templates. For this time, clicking and confirm at the bottom of our form. We'll then go ahead and delete our hotel rather than update. Add_hotel as in our object, the title of delete hotel, an then passing our hotel variable. The reason we're using this add hotel template once more is so as you can see all of the hotel details influencables to make sure that this is the one we want to delete. We also pass in this hotel, which we're going to be deleting. Once again, you can populate all fields inside of our form. Now if we say this makes you on the same routes as before, which is admin, our hotel ID, and then delete. We can now reload and now we get the add hotel form. Now what this mean? Now we have this get request all setup. We need to now handle how we delete the hotel. To do this, we can create a post request to the same route. Over to the index.js, let's duplicate this line here, paste this in, this time this is a post request and delete hotel post over to the hotel controller and this is exports.delete hotel post. This is also a sync function since we're dealing with data from our database, passing request, response and next. Next thing to do is to add our error handling we try and catch. I just noticed above, we also need to add this into that too. Just for moments will ask the error and then pass our error to next and then we can add this to delete hotel get too so try. We can add these three lines until a try block and then add catch and emphasis to next with our error. Good, so back to our post requests below inside of the try section. Again, we want to grab the ID from the parameters so we can copy this line here. This in an asset of our hotel so const hotel awaits, our hotel model and to remove an item from [inaudible] , we can use a method called find by ID and remove, which is fairly similar to the one we used above, but this one was find by ID and update. Down to our post request, we can again find by ID and remove. Then inside here all we need to do is pass in the ID of the hotel we want to remove. ID, the field we want to target, and then matches to our hotel ID, which is this constant just here, so semicolon at the end. Once we've deleted this hotel from the database, we'll then want to perform a redirect. Rest or redirect and where do you want to go too? Well, before when we're looking at updating hotels, it made sense to redirect to the current hotel with the updated information. However, we can't do the same when delete in a hotel because this hotel no longer exists. Instead, I'm just going to redirect to the home route, gives us a save and then over to the browser. If we reload, so now we're currently on our test hotel and now for click ''confirmed'' creates a post request. This is a good sign were being redirected to the homepage. Now over to mlab refresh. We now down to 12 records so we have hotel 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. On the next page we have 11 and 12, so it looks like our hotel has been successfully deleted. If yours been deleted, congratulations, you have now performed create, read, update, and delete actions on your database. This is a big step for our application and we've made a lot of progress. In the next section of this course, we're going to be pushing on with our projects by adding, styling, more templates and features and generally making improvements. look forward to seeing you in the next section and bye for now.
31. Hotel detail view: In this section, we're going to be pushing on with our projects and doing some general work, includes styling and improvements. I'm going to kick things off by working with the hotel detailed templates which we created to display the hotel we searched for before editing or deleting. We're again going to reuse these templates in a few places. First of all, if we go over to the homepage or the home routes, and then click on any of our hotels, go down to the bottom of our templates, we see an error message of not found. This is because we need to handle these routes which you have at the top of forward slash all, then forward slash the hotel ID. With these few, we can reuse the hotel at detailed templates which we created and also adding these routes will fix other areas too. For example, if we go back to the homepage and then you click on, see all, clicking on any of these hotels is again taken to the same route. So click on hotel one and we have the same routes of foward slash all and then our hotel ID along with the error at the bottom. This will fix both of these problems, and also again, if we go over to the hotelController.js and go to create hotel post. Scroll up, so you create hotel post here and this res.redirect, also redirect to this same routes here. This is after we create a new hotel, will then redirect this hotel detail page. This is a good one to get working now. Let's start with the route over any index dot js. If we scroll up, we should already have the route or the get to the forward slash all route. Let's keep these in sequence and add this below. So router.get around this time, as we know, is forward slash all, and then the hotel ID, which you can add as a dynamic segment. I'm going to call this variable the hotel. This is also going to use the hotel controller and then use a function called hotel detail, semicolon at the ends. Then we can go ahead and create hotel detail over in the controller. We can add this anywhere, let's go down to the bottom. We start as always with exports, and then the name of hotel detail, and then set this to a sync function, pass in the request, the response and also next, add the body of our function. Then I'll try catch block. So try and then catch which takes in the error, and then pass the error to next. Okay, good. We're going to begin by creating a couple of constants. The first constant is going to grab the parameter, which is the hotel ID. We're going to use this hotel dynamic segments, which is in the URL. Let's first say this is in a constant. So const, hotelparam is equal to the request dot params and then dot hotel, which is the name we gave it. A second constant, which is for the hotel data. We're going to use this to search our database using this hotel parameter and then find the unique hotel which we need. We're again going to use await, to wait on states are coming back before moving on. We need to search for the hotel model, capital H, use the find method. Then we need to pass in the field that we want to find. We want to use the underscore ID fields and check for any ID's which match our hotel param variable. Pass in the hotel param, add a semicolon at the end. Then to finish this off, we're going to do a res.render to render out templates to the screen. The template we're going to use is hotel underscore detail. Pass in the title of let's travel and then pass along our variable of hotel data, which is this variable here, which stores the unique hotel from the database. Now since we already have hotel underscore detail inside of our sidebar and then in the views. We have this views down here. We only have this setup and this is also receiving the same variable name of hotel data. Now if we give this a safe, say the controller and then go back over to our browser and now reload. We now grabbing the hotel ID from the URL personally to our controller, which is then getting the information back from our database and then sending this to our hotel detail template. We should also see this is working if we go to the homepage and click on any of these hotels, to be taken to the hotel detail page. Let's also test the routes, which is all the hotels. Click on see all, and again, any of these hotels, and this one's working fine too. Finally we can go to add new hotel. Go to our admin forward slash at and let's create a test hotel. Any information in here is fine. Then confirm, scroll down and there's our test hotel, which we just created and as we've seen before we get a res dot redirect, which goes through forward slash all and then this hotel ID. Although this hotel detail view at the moment, looks the same as all of the hotels, there will be some differences soon. When we go ahead and add some styling, multiple hotels will fit on the same page just like in this homepage here. But if you click on this and look at the hotel detail view, this will be a single view for just one hotel. This will also give us the space to add more details, such as a hotel description down near the bottom, and this is what we will do in the next video when we look at conditional rendering.
32. Locals and conditional rendering: We're going to cover a few different things in this video. Start with locals, all local variables. This is something we've already covered really when templating, but we're going to also look at how to make these available to all of the templates too. Currently over in the hotelController. Let's click on the hotelController.js and go down to the hotel detail. Just here, we already passed a local variable to our templates, just like we've done many times. The data which we passed into this template, if we add this to it's own line to make it a little bit more clear, this is a object which currently we have the title, which is a name value pair, and then also a hotel data which is a variable, which we created just above. Here we're passing in the title and the hotel data which we pulled in from the database. We are free to use these locals to pass anything we want to use in this template. For example, we'll go ahead and add the name of Chris and then over in his hotel detail view. We've already seen we can use these just like normal JavaScript variables, just like we did here, using hotel and then accessing the ID from the object. Here we're using the local variables with back ticks, so you can mix them with some text. We've already also outputted these variables with the equals symbol. So we know how to do that. It's p equals, and in this name, reloads and there we go, that's our local variable as the templates, and also how we can output a variable when used with plain text using a hash. Let's remove this. If it mixes with some plain text, such as here, we can use the hash, the curly braces, and then also pass in our name variable, save and then reload. There we go. There's our Update hotel and our name of Chris mixed in with the button text. Let's remove these examples. If we go over to any of the templates, for example, if we go to all hotels, let's try to do the same. Let's try to output our name variable. So p equals name, and go out to all hotels routes. Click on see all. Now if we scroll down we don't see any reference to the name. This is because locals, as they sound, they are local variables scoped to only a single template, which we'll pass them to, so let's remove this, p equals name. Also in our hotel controller, let's remove our name from here too. But it also often cases when we want to pass certain variables to all of our templates to use rather than creating a variable in each one of these functions and then passing the same data. If you remember from early on in the projects over in the app.js. Let's go to app.js. We talked a little about the app.use middleware, which will have just here. This middleware, when we set no routes as the first parameter, is an action which is used for every request to our application. It basically adds a layer which we pass through for each request. We can make use of app.use to create some middleware for all requests, which will then go ahead and make our variables available in all templates. So let's create this within app.use. Let's go up to our app.set and then add in our app.use middleware. This is going to take in a function which has access to the request, the response, and also next. Let's set this up as a normal function. When using app.use, this function, which is called will also have access to the request and response objects too. Remember though if we only want this function to run on a request to a particular route, we can add the routing first before this. So as the first parameter we can add the string, a forward slash admin for example, separated by a comma, and then our function as the second value. Now, anything we run inside of here will only work on this admin route. But we want this to run on every template in our app. So I'm going to leave the first parameter empty. See there's an action we can add a console log inside of the function body, add console log, and then simply add some texts of hello. Give this a save and then over to the browser. Now, if we click on our logo to make a request to our site, over to Visual Studio Code and now we can see down at the bottom we have the texts of hello, retries a few more times. So keep clicking on this to make a request. We see for every request we get the console log of hello. Inside of the browser though, if we take a look at the spinning icon at the top, we see our app begins to hang. It's because middleware is meant to be passed through and here we have not added in the next call to move on to the next piece of middleware in the chain. So it's basically stuck on it's route. Let's go over to our app.use, and then inside our function, I'll hit next, give it a save. Now if we reload, we can see we can reload this route as many times as we want and it's still working. So over on, the request object which we have here, we have access to a property called the path. This will return the path name of the current request or basically the current URL. Let's take a look at this. Let's say the current path is, and then add on to the end the request, which is this object just here dot path. Now give it a save and then go to any route. For example, let's go to forward slash sign-up, hit Enter. Now in Visual Studio Code, let's lift up this terminologies here, and we can see here our current path is forward slash sign-up. This is the console log which we set just here. I am now going to make this URL available to use as a variable which will have access to any template. Therefore, we can do some conditional rendering later on, which will allow us to only display certain information based on the current route. First of all, let's reduce these down to make some more space and delete our console log, leaving in only the request dot path. Now this request dot path can then be assigned to res dot locals. So res dot locals equals the request dot path. This will then assign our request dot path to the locals object, which is on the response. We can also give this particular local or this local variable a name of our choice. To keep it descriptive, I'm going to call this dot URL. Therefore, we can access this variable or this path in our templates by its local name of URL. What does this mean now for our projects? Well, it now means we can access this URL local in any of our templates inside the project, because it's middleware, which we set up just here, is always passed through for every request. We will also be adding more locals just like this as we go for this project. For example, to make flash messages available in all templates too, but this URL variable, we can now use it for conditional rendering. Conditional rendering allows us to display certain data depending on a condition. Just like when we use a if statements. It will be really useful for us in this project. Since we are reusing templates, we can now only show certain parts of the templates, for example, on certain routes or could show or hide certain parts of that template, depending if a user is logged in. The first thing I want to do is remove the update and delete buttons for certain routes. So if we go to our homepage and click on any of these hotels, and I scroll down, we see we have this Update and Delete Hotel button, which is showing on this route here. This is something we only want to show in the admin routes. To hide these, we can make use of conditional rendering to only show if the URL or the path begins with forward slash Admin. So over to the hotel_detail.pug. Save our app.js. Let's first begin by creating an if Statement using our URL variable. Just after the dot hotel div, indent them in one level if url, which is the name of the local variable, which will now pass to all templates and then use dot startsWith. Add the brackets afterwards. StartsWith is a regular JavaScript method which will be true if we pass in a string inside the brackets, which matches the beginning of the URL. So to check if the URL begins with forward slash admin, we can add this in as a string. Now, if we indent these four lines here, which are our buttons. Now if our URL starts with forward slash admin, everything which is nested inside this if statement, i.e our buttons will now be conditionally rendered otherwise, anything else which is not nested inside, such as our hotel mixin, will still display regardless of the route. So over to the browser and let's give this a try. If it reloads and then scroll down, we can see these buttons are now missing from the top of our templates. Let's try some more, open the homepage, see all, click on any one of these, and the buttons are now still missing. Let's try the admin route and check the button if they're there. So forward slash admin, and then go down to Edit, remove hotel. Let's search for any hotel name. So hotel 7, this now displays hotel 7, and we still have the updates and delete buttons. Because our URL begins with forward slash admin. Good, this conditional rendering is now working with our URL variable. Another use for this variable will be over in our hotel.pug mixin. So let's open up the sidebar. Mixins and then hotel.pug. We can finally make use of the hotel description we have available, but only display this in needful hotel view. So just after the hotel name, keep any horizontal line. Let's create another if statement using our URL. So dot startsWith, this time we can pass in a string which is forward slash all and then forward slash. Nested inside here we can set a p element which is equal to hotel. So it's hotel_description as a new horizontal line just below to separate this from the star rating, the country, and the price. Save this and then over to our browser. Let's try with the homepage. Now we see our hotel is listed with no description. However, if you click on one of our links to go over to the hotel detail page. We now see description is now added into our templates, but because we only forward slash all routes. If we go over to our homepage and then also click on see all. Here we also see all of our hotels, but with no description. But this routes also begins with forward slash all. This may seem a little confusing why we're not seeing the description on this page two. If we look a little closer and go over to our template, we see the trailing forward slash after all. If we go over to our routes in our index.js, having forward slash all, and then forward slash right at the very end only matches this second route on here, which renders our hotel detail. Since we using a additional forward slash and also our all hotels route here is only forward slash all with nothing at the end, this is why this description only works on the hotel detail page and not on the all hotels route, which we have here. We'll make more use of this URL variable for the course, but next we're going to be handling display in our hotels based on a particular country.
33. Hotels by country: If we head over to the homepage of our projects and then scroll down, below are hotels, and go down to other countries, which we have just here. We have a list of countries which have hotels located inside. If we click on any individual hotel link, such as this, this will take us to a route of /countries and then /country name. Currently we get a 404 because its route is not yet handled, as we see here, we will be fixing these in this video by displaying a template, which will display all the hotels in the database which are located in this particular country. As usual, let's get to work over in our index.js. Let's clean up some of these tabs for now. Index.js. Let's go down to our /countries and then add a new route which is below this. Since we are just getting a template, we can use a get request, so routed or gets, and then we can use /countries, just like we see in there. Then add our dynamic country name as a parameter. That's the colon, and let's call this the country. This is also going to use the hotel controller and then we can create hotels by country. If routs has a dynamic segments to capture the country from the URL. Let's go to our hotel controller. Create hotels by country function, and then we can grab this dynamic section from the URL. Hotel controller down at the very bottom, we will set this up as normal, export starts. Hotel by country will mark this as a sync, passing in the request, the response, and also next. Let's add our try section. Catch any errors and then as usual, we'll pass this to next. We'll begin by capturing the country from the overall parameters, and this is just like we did before with the hotel. Let's set obey constants called CountryParam, and this is the request..Params.country. Then we can setup our find method to search our database by the country. Columns, we'll call this the country lists, because it's going to be storing a list of countries which match our parameter, we will await Hotel.find, and then to only return our countries, we can pass in our objects passing the Country field which you want to match to and then match to our countryParam variable. Before we pass this data to our templates, let's first do a res.json to see what data is being returned from the database. res.json, and then pass in our country list, which should store a list of countries. It gives us the save and then reloads and make sure you still on the same routes as before, which is /countries and then a particular country just afterwards. Since we're on this USA route, we only seeing countries available in the USA. The test hotel were created before, was the country USA, and this hotel seven is the USA too. You may only have one per country depending on which country you clicked on. We can also change this in the URL. Change Mexico, and here we go. We'll have hotel nine and also hotel 10, which are in the country of Mexico. This is looking good. We now have the hotel data we need to pass to our templates. Now we can replace our res.json with a res.render. We're going to be creating a template called hotels by country. Using the underscores separated by comma, we can adds our objects which becomes contain the title or browse by country, and then also inside here we can add a dynamic section, which is going to be the name of the country which we're looking at. We're at this with the symbol, the curly braces, and then inside here we can add in the countryParam, and since we're now using some dynamic data in our string, we also need to change these quotations to be back ticks. Change these are the semicolon at the end and as well as its title. We can also add a comma and also possibly in our country list, which is the data and we're going to be needing inside the templates. CountryList and give us save. The final stage, as you already know, is to create these hotels by country.pug File, and make sure you spell correctly. Opened up the sidebar inside all views folder, create a new file called hotels by country.pug. This template like all others will extend our layout and we'll replace the block called content. Since it's templates will also be displaying the list of hotels, we also need to include our mixins. Include Mixins/_hotel, or hotelmixin. We can also pass in our title, h2 equals title. Now we need to loop through all the data in our hotel controller. Who seen in this country data is stored in a variable called countryList. Let's create a loop. Each hotel in countryList, creates a wrapper of.hotel_wrapper and impulse in our mixing with the plus symbol, plus hotel, and also passing the individual hotel data from our loop. We can also add a else block on here in case no matches are found for this country, display this on a list item. There are no hotels, and once you have a template which looks like this, let's give this a save and then reload. Now I'll res.json is replaced with our templates. Have a error, let's lookup the view hotels by country. Let's see what's going on. We have a spelling mistake, so hotel, and this is hotels, so just change this file name inside of our views. Rename, I just add a S on the end of that. Reload the browser. There's our dynamic level two heading or browse by country, when impulse in the country name, and there's a hotel nine and also the hotel 10, which we seen before inside of the JSON. Let's test a few more countries and see if this is all working. Let's go back to the homepage. Down to the very bottom. Let's try Jamaica. We have the dynamic title, and this country is in Jamaica. Down to Netherlands as our title, and our hotel free, which is inside of the Netherlands. Excellent, this is another step in our project, which we've now completed. I think now we have a lot of this working. We should now take a break from adding new features and go ahead and add some CSS styling to make it look a little nicer.
34. Styling the header- small screen: Now is the time to make our project look a little nicer with some CSS. I'm going to begin by styling the header section for the small screen view. Let's begin by shrinking down the browser. So shrink this down and then over in Visual Studio, we can also make this smaller too, open up the sidebar inside of our public file or public folder. Let's click on this and then we have a style-sheets folder with our style.css, and then close the sidebar. Inside here we have some defaults, styling, which is provided with the express generator. So I'm going to select all and remove. To begin, I'm going to add some general styling to the HTML on body sections. So as the HTML selector and then declare we want the background to be white. Then down to a body section and of course, feel free to make any changes to make this app more personal or follow along with me if you prefer the same styling. So the background for the bony section, I'm going to give this a value with the hash of eee, the center this inside the browser and we can use margin zero also set the maximum weight of the body to be 1500 pixels. This will make sure that on really large monitors, the content isn't too stretched and finally, some padding on the boundary of zero on the top and bottom and warn M on the left and right. So we save this and reload. We shouldn't see too much of a difference for now, but we can just see the gray background color, IPO decentralized and also a little bit upon him. From the top I'm going to begin with the nav links, which are these ones just here and these have a wrapper of nav inside here. I'm going to change the display type to make use of the flex-box. So display flex, and then we can set the flex direction to the column. This will set the flex items from the top to bottom. Also the align items property to the center and also declared the background color. So much the logo. So set the background to a volume using the hash for the hex values of 4dc2ca Reload this. There we go. So now our items are now centered in the middle of the page, and also have the same background color as a logo. It looks as though there's also some defaults, margins and plains on here. Let's get to work with our unordered list. First of all, remove any default put in a URL, and then uses just over to the left. The individual list items. We can make these a little larger by setting the font size to be 1.2 em. The display to be inline-block and then also the margin of zero on the top and bottom and ten pixels on the left and right. Reload. Okay, good. So now our list items are inline block, so they displayed across the page in online formats and also they are still below the logo because we said the flux direction of column on the nav elements, which includes the logo and also our list items. So down to our list items here. We can then target the links too. So first of all, if we set the text decoration to be non saving reload, and this will remove the defaults on the line from our links. We can also remove this default color by setting the color value, I want to use a RGB color. So the first one is red value of 43, 40, and 40. The green and blue refresh, which will give us this darker gray color. If we go over to our layout output file. So open the sidebar into the views, and layout that hook up to a form at the top. We added a wrapper classof.search nav, which is the main wrapper for all of these form items inside of our header and also each individual inputs also had a wrapper of dots input rapid too. So we'll go back to our style.css and begin working with our form inside the header. First of all, the main wrapper which was search nav. So search_nav. The backgrounds. I'm going to set this to a hex value of, c1e6e9. then make sure this is spelled correctly. Give that save and now we'll have the lighter blue color 404. A little padding inside here too, just add some spacing of 0.5 em. So next I'm going to move on to all of these items in sort of a form by setting the display type to be flex. We can also give these a flex direction of column for the mobile view. Later on, we'll also change these to be back to the default row, which will then display these across the page when we have more available space. So let's target these in the CSS. We've got search_nav and we can only select the form which is inside of this class here. Meaning the styles which are inside here will only display to this particular form and no other forms on all sides. So display type or flex. The flex direction of column. So this is a fact and some retriever and then we can go ahead and target the individual inputs. So the individual inputs for the smallest green, we won't need to be the full width of the page. Set the width to be 100 percent. We can declare the heights of 20 pixels. Save this, and reload. Okay, good. Let's also add some margins and patterns to give you some space into. The padding. So 0.5 em on the top and bottom and zero on the left and right. Skewed easy full width. So also some margin on the top, space out from the ones above and we all need a little bit of margin of 0.3 em. Save and then reload. I'm also going to remove this default border around the inputs. So let's set the border to be a value of zero. Reload, and now we have this gray line removed from each one of the inputs. At the moment on the mobile view, if we type into one of these destinations, the text line is over on the left-hand side. I'm going to change this to be in the sensor by using the text align property of center and also add some border-radius. So each one of these inputs of five pixels. Save then reload this and now we can see how text is now centered, and we have a little bit of radius on each one of the inputs. Then down to the input refer, which surrounds each one of our label on input combinations. Just to add some margin on to the top and bottom to out some foam spacing and also align this text inside the sensor for the label. So targets the input on a scale wrapper, the margin, the top and bottom values of 0.5 em, and then zero on the left and rights. We can align our labels with a text line of center. Save and then reload. Codes now look a lot nicer inside of the mobile view. We can now add some finishing touches to the select inputs and also the button's down at the bottom. We can select these by the element's name. So select and button. Let's declare the heights of 36 pixels. The width on the small screen of 100 percent. So much of the rest of the inputs and also remove the default border by setting to be none. Save and then refresh. Okay, good. So we needed an alpha of a form. The last thing I want to do is to target just the buttons to give this a background color of cadet blue and also give us a border-radius of five pixels. So much all the rest of the inputs. Okay, reload. Good. Our header section on our form is now looking a lot nicer on the small view. If we scroll down the final touch I'm going to make in this video is to set these header beach image and also any other images on the site to be 100 percent to benefit the container rather than overflowing like we see if we scroll to the right. So down below the button, select all the image elements. Set the width to be 100 percent. Refresh, and it instantly looks a lot better now. Good. Our header is now looking better on the small screen view next week click on to continue we will be styling for mobile sizes both apply these to a hotel's and countries.
35. Styling the content- small screen: With the header styling now complete for our mobile view, we can now move down to a style in the content area, which has the hotels and also the countries. If we go over to our index dot pug. Open the sidebar into the views and then our index dot pug. Over on the side here we have a outer wrapper, which is this one just here. Then nested inside we have a hotel wrapper which goes down the hotel section, and then a country wrapper, or the country section down at the bottom of our index page. Let's go to our style dot css and begin working with the outer wrapper. Make a comment. This is the content section. Hotels and countries, view. Okay, so let's start with the dot outer wrapper, with the under score. This is a wrapper for basically all hotels and countries we see over on the homepage here. We're going to use it to align the text to the center and also set the font size. The text-align property, of center and a font size of one em. Set this, and then reload. Now we can see our title and also all hotel information is now aligned to the center. Now on to the dot hotel div. This was the wrapper we used inside of each hotel. If we go to the mixins and then hotel dot pug, we gave each hotel a wrapper of dot hotel so let's use it now in the css. Below outer wrapper target the hotel. Okay, so we're going to give each individual hotel a background color of white, so it stands out against the Grey background. Background of white and also some margin of zero on the top, zero on the right, one em on the bottom, and zero on the left. If we now save this and then refresh, we now see the white background color and also the margin on the bottom separates out each hotel. Also inside of this mixin, we have the text fields inside of a div called hotel info. Back to our dot hotel dot pug, we have this hotel info section which contains the hotel name, and also in the hotel detail, we have the description and also the star rating, the country and the price. It's basically all these texts we see in each hotel. At the moment, if you click on any individual hotel and then scroll down, were then taken to the hotel detail view. All the text inside here is close to the edge. We can fix this with some padding. Style dot css, the hotel underscore info. All we need to do here to fix this, is to add some padding of one em and then reload. Now have some spacing around the title, the description, and also the information down at the bottom. Next, I'm going to move over to our forms. If we go over to our admin, we should make this a little bit bigger so we can see the URL. Admin and then forward slash add. Scroll down to our form, and currently our form doesn't look too great. Let's get to work on this now. Shrink the browser back down. We've already added certain class names inside of our views. We go to add hotel, close this down. We have this hotel form which is rendered as a mixin. If we go over to this we have each individual form group with the label an input surrounded by this form input wrapper. This can be used over in the style.css. First of all, form underscore input. We can add some margin. If we add one em and then zero, this will add one em of margin to the top and bottom. Give in some space in between each input. Then we can go ahead and target the individual input and also the text area. Because we want this label to appear to the left of each input. To begin, if we set each input to be the width of 70 percent, to make it a little bit narrower, this will then give us the space to add a label on the left. We target the form input, and then each individual input and then we also need to target the text area too. Set the width to be 70 percent. Also the text line to be on the left. Save and then refresh. Okay, that's better. Now on to our label. Again, the form input. But this time we're going to be targeting the label. Set the display to be inline-block. Then give this a width of 20 percent to make sure this fits on the same line as the input. If we reload, we now have each label to the left of the input. Onto this text area, we remove the border from all of the rest of the inputs and give these a border-radius of five pixels. Let's also apply this to the text area so it matches. The border of none and then the border radius of five pixels, semi-colon, and then reload. Now it's exterior matches the rest of the inputs. Then finally down at the bottom we have this large confirmed button inside of the hotel form. If we go down to button at the bottom, we gave this a class of button small. Let's copy this and over in the style dot css add the dot and paste this in. Let's make this smaller by adding the width of 100 pixels, and also some margin of 0.5 ems to give it some spacing, below the browser. There we go. Now the back to admin button and also our confirm button at the bottom is now using this button small class. Now this looks a lot nicer for our small screens. Back to the home page and scroll down and everything looks pretty much as it should be. However, though, if we stretch the browser, make this real wide, things begin to look a little too stretched. This is something we will move on to in the next video, where we'll begin styling the larger view by adding in a media query.
36. Large screen styling: If we make our project the full-width inside the browser, it now looks a little stretched because so far we have focused on the small screen sizes. Let's now add some CSS inside of a media query to fix this. Up to our style.css, right at the very bottom of the file, we add the media query with a media. I want to be targeting the minimum browser width of 1,000 pixels. Open up the code races. You can have a play around with what size works best for you, but I'm going to go for a 1,000 pixel breakpoints and then begin at the top with the navigation. Select the nav elements and make sure this is inside of these media query curly braces. Now we're on the largest green. Up at the top, we have the top header section set to be a flex direction of column, meaning the logo and also the nav links are unsalvageable. Now I'm going to change the flex direction to be back to be row, meaning the logo would be on the left-hand side, and then the links will be on the right. We do this with flex direction and satisfy row, reload. Inside of our navigation section, we have the nav, which is a link for our logo, and also have the another list for our two links here. I want to start the flex value for the logo to be a value of one, and then the another list to be a value of two, meaning it would take up twice the available space over on the right-hand side. We can then align the texts over to the right to make sure that this is over on the right-hand side of the header. Let's begin with the nav a. If it goes to the layout inside a here we have nav and then the a nest inside, and this is for our image logo. Then the another list which is not the same level, so this is going to be a flux value of one, and this will be the flex value of two, making it twice the available space. Inside of our nav a, this is going to take up the flex value of one. Then our nav ul will be the flex value of two. Save this and reload. We don't see too much of a difference but if we go into the developer tools hover over our nav, we see the blue outline for the one part. Then if we go to the another list, we can see this is twice the available width. Back over to the styling. After the flex, we can add the text alignment to be on the right-hand side, pushing over to the edge of the div. But this is probably a little too close to the edge, so a little margin right will fix this of one m. Reload. Now we'll have a nav bar more suited to a larger screen. Good. Now moving down to the search form below. Now we have many columns setup like this is better for the mobile view. On the largest screen like this, we want to change the flex direction once again to be row, so it's more suited to the more available space. Under our nav ul, let's target the search on the score nav. If we go to the layout.pug and then down to form, this is the outer wrapper for the whole of the form. After style.css open up the curly braces and then we can set the flex direction to once again be row. There is no change. In fact, we just needs to also target the form inside here and try this, and there we go. Let's continue with this flex styling. Justify the content to be in the center. This will give us equal spacing on the left and right-hand side. Then after this, the flex wrap, we're going to set this to a value of wrap so it can flow onto additional lines. Then finally, we'll align the items to be flex end. This will align all inputs vertically. Now if we save this one I'll push these inputs to be now in line with the rest of these. This gives us the horizontal form which we want, but it's still a little squashed up. Only when creating this form which surrounded each input with a div, with the class of input Rapa. If we go over to our layout of pug, inside of our form, we can see this just here. This is round and each one of our label and input groups. Over to the style.css and again, still inside this media query, we can now target these classes which was dot input_wrapper. Let's set the margin to be zero to remove any defaults. The Padian to be 0.5ems. Save this and then reload. There we go. We can now see the pattern has added some space in between each one of these inputs. Also restrict these to be a maximum weight of a 170 pixels so don't stretch too far. Then also set the text alignment to be back to the left. Wait for reload and now we'll have some additional spacing around each one of these inputs. Scrolling down to the hotels and countries, the text for the headings which you see here and also for the hotels, it's still inheriting the center alignments in the mobile view. I wanted is by targeting the outer wrapper div. Remember earlier we said the outer wrapper savvy.outer wrapper class. If we scroll up this was one just here which wraps all the hotels and countries on the homepage. Scroll down, we can override the text alignments inside the media query dot outer_wrapper, we can reinstate the text alignment to be on left. Refresh. Now onto each individual hotel. I'm going to be using the flex box for each one of these hotels. Let's set the display type over in the CSS. After outer wrapper, select the dot hotel, which is the div, which is surrounding each hotel in the mixin. The display type of flex. Then reload. This is how our hotels now look by default. It still needs a little work to look good though. If we go over to our hotel mixin, which is underscore hotel.pug. Make this a little bit larger. We added classes to both the image section, which is hotel image just here, and also for the inflow section. We can now use these in the CSS to make our hotel's look a lot nicer. Back to the style.css. Let's first go for the hotel img. Give us a flex value of one, and also some margin of one m to give some spacing. Save and reload. We can just about see the margin on the outside of the image. Now onto to the hotel info, which is the text on the right-hand side. So.hotel_info. We can also give us a flex value of one to make this equal to the image. Then some patterns, add some spacing zero on the top and bottom. Then one m on the left hand right. Save this and then reload. Go to homepage now looks much better. However, if we click on a hotel and go up to the hotel view and scroll down. The CSS we added also applies to this full hotel detail too. I only want this image and hotel to be side-by-side. When we list all the hotels on the homepage and also in the all hotels view. First we can restrict the CSS to only apply to the all hotels view over in the all hotels.pug. Let's take look for those templates. All hotels.pug, let's open this up. We surrounded the hotel's inside here with a.hotel wrapper. Meaning we can use this to be more specific about where we are applying our styling. This can be done by adding this wrapper class before the hotel class in the CSS. Go back to the style.css, let's copy this. Then just before the.hotel, let's add in the hotel wrapper, giving us a more specific CSS selector. Save this, and then it reloads. Still on the hotel detail page. We've now removed the flexbox from this view meaning we now have the content stacked on a full-size image. Let's just check over in the homepage. It still looks okay. Thank click on all hotels, and the flexbox skill takes effect in this view too. To finish things off, and I want to display the countries as a grid. If we go over to the homepage and then scroll down to the very bottom. We have the countries down at the bottom here, but the don't look too great. There's two places where we display the countries. He on the homepage and also if you click on see all, in this link here. This is pretty much the same view as the homepage. Both of these templates have a div with the class of country wrapper. First is over in the index.pug. Open up this in the views index. pug. We have contour upper down at the bottom here, surrounding our countries, and also in the all countries templates we have the same div just here to keep things consistent. Also these countries are outputted as a another list, which we can see here.. We can use this over in the CSS. Back to the style.css and then down to the bottom on the hotel info. Let's select our country wrapper. Another list. We can set the display type to be grid. We have three columns because as the grid template columns to repeat. Then inside the brackets we wants to repeat these three times using the one fr unit to make these equal. To space them out, we can also add the grid gap, which looked at early in the course of 20 pixels. Then also align the text to be in the center. Save this and then let's check this out in the browser. Reloads, and this is the forward slash countries routes, which seems to have some bold text from nasa will take a look at this. Then if we go to the homepage and right down to the bottom, we also have the grid for the homepage too. Let you check out this bold text. Let's click on see all. We can see it's just here. Let's go over to the all countries.pug file. I think it's just a indentation problem. We've got a level two heading here, and then inside of the h2 we have everything nested inside. Let's move all this over to be the same level as the h2 rather than nested inside and reload. That's a lot better. Click on a country. Then we're taken it to the country's view where we see our two available Dominican Republic hotels inside of our list. Once again, we can see how easy is to use the CSS grid. We have nicely aligned a grid layout with just a few lines of code. We'll come back to the CSS later on and add a few more changes as we go. One thing you may have noticed here is when dealing with the countries on the index page and also in the all countries templates, if we'll go over to our template here, we're reusing the same code flow to different templates. We'll fix this in the next video by moving our countries into a reusable mixin.
37. Countries mixin: When we have repeated code in multiple templates, it's often best to put this into a separate mixing, and this is what we'll be doing now with our country's list. In both the index.pug. Down at the bottom we have a list item which I'll puts our country. Also in the all countries.put templates, will have the same list item, displaying our link and our country image. In both these templates, we'll loop through these countries and display both the image and the name as a list. We already know how to use mixins. Let's go ahead and create a new file in the mixins folder to display these countries lists. Open up the sidebar. Inside of the mixins folder, let's create a new file. Underscore country, underscore list with the.pug extension. Let's give this mixin a name. So mixin country list is mixing moving past a country to output. So we can also add this variable into. Then save this file. Then over in the all countries.pug file, we can cut out the list item, the link, and also the image. Grab these three lines just here code this out and then we can go ahead and add these to our country mixing, paste these in, and makes sure the indentation is correct. We will move the list item too, so it's not at the real level. So this is our mixing now looking fine. We can save this, and then go back over to the all countries.pug and then up at the top we can include the mixin file. After the extends layouts, include the mixins, forward slash and then underscore the country underscore list. Remember you don't need the pug extension. Then we can add the mixing in by its name in place of the list item, which we put out indented in one level. Inside of our loop, we call this the country list. This country list is also going to take in the country, which is passed from our controller. We can now save this file and then go off to all countries, which we're currently on now. Reload this. We see the countries are now in place, but this time being pulled in from a mixin. Now all we need to do is repeat this for the index.pug which is this file just here. First of all, we can include the second mixin. So include mixins/. We also have the hotel mixin, but this time it's the country underscore list. Then we can go down and add this mixing in place of our list item. First of all, remember in this file we access the country name with the underscore ID and the link and also the image. This is because inside of our hotel controller.js, if go to the homepage filters, we group together the distinctive countries with this group in stage with the key of underscore ID. Mainly, we'll process ID into the mixin for it to work. So let's remove the free lines from the very bottom. We can replace this without mixing of country list, and we also put in our country._ID. If you save, now this is for the homepage. Go to the index and then scroll down below the hotels, we now have our full country list on the homepage too, as well as the all countries list. Meaning our mixing is now working and we've reduced our duplicate code.
38. Promise.all and array destructuring: Inside of the hotel controller, we created the homepage filters early in the course. You may remember that the other time we said that we're using multiple await calls, just like we've done here, is not always a good idea. Doing this is perfectly fine, however, if the second await call relies on the first one finishing before this runs. For example, if the first await at the top here, save the hotel, and then the second await call needed to access the hotel ID, it would then make sense to keep them both in this way to ensure that we have the hotel ID ready for the second one when needed. But here, we are blocking, fetching the countries from the database until this first await section has finished, and this makes no sense. What we really want to do is kick these both off at the same time. For this, we have something called promise to all. Promise to all is a method which basically is a giant promise which wraps all other promises inside. You maybe thinking, "What does promises have to do with async await?" Well, async await also returns a promise too, so let's take a look at this in action. First, let's remove the two awaits keywords as we don't need these anymore. Now, we're going to look at a few new techniques which will introduce in ES6 or ES2015. First is what is called destructuring. More specifically, for this example, we're going to be looking at array destructuring. We can use this to basically create an array of constant names, then assign these to an array of values which we unpack. It sounds complicated, but it's pretty simple when we see it all typed out. First, let's create a constant to begin with. After our countries, just down here, let's make some space, create a constant. This time, this is going to be an array of values. Passing two values, the first one, I'm going to call filteredCountries, and then the second one, filteredHotels. I have named these countries and hotel variables filtered because these results are filtered using the aggregation pipeline, then we use promise to all which is also new in ES6. This equal to, now, we're going to await, promise with a capital P,.all. Like we said before, Promise.all takes in multiple promises, so it can pass in our countries and also our hotel variables inside here. First of all, let's pass in the countries and this needs to be an array, and also our hotels. Now, this Promise.all, which we have here, will now be resolved once both the countries and also the hotel promises have successfully resolved. Whereas before, using await individually, rather than just once here, this caused both of them to run in order. Now, we can set both these off at the same time, resulting in faster performance. As for the array destructuring, these countries and also the hotels promises, will then be basically unpacked, and installed in the constant names which we set here in the same order. The countries will then be unpacked, installed into filteredCountries. Once resolved, also the hotels will then be stored inside the filteredCountries constants. Let's take a look at one more example to get our head around all this. We can leave this code in because this is what we need for the course. But this one is just going to be a simple example, so you don't need to type along if you don't want to. A consonant of food. Let's set this to an array and add some food values of cheese, fish, and also rice. Then a constant of a, b, and c. Now, if we want to destructure all the values from this array and install them inside of all three values here but in order, so a will become cheese, b will be fish, and c will be equal to rice. All we need to do is to set these values equal to our food array. Then a series with a res.send. Let's send the value of a. Check if this is working and comment out our res.render. Save this and we're also on the homepage. Let's go to localhost:3000. There's the value of cheese. Let's try b and also c, which should be rice. Reload. Now, we get the value of rice too, meaning our food array is now been unpacked and then stored into all three constants just here. This is pretty much the same thing which we are doing above with our filtered variables. Then we are setting these to our countries and hotels promises once the data has been resolved. Now, let's leave this example and [inaudible] our res.render. Good. Now, we have our two constant names, our filteredCountries, and filteredHotels. We also need to pass these to our template in place of our old constants here. This one is filteredCountries and this one is filteredHotels. Also since these variable names are now changed, we also need to change them in the template to match. The template is the index.code. We go into here. For the hotel, instead of looping through hotels, we need to loop through filteredHotels. Then do the same with the countries. This time, let's use is filteredCountries. Then save. Now, all that's left to do is to try this out over in the browser. On the homepage, hit refresh. We can still see our hotels being pulled in from the database, and also the countries down at the bottom too. This means both our destructuring and also our Promise.all method is now successfully working. How many hotels we have inside of our database, changing both our promises to run at once rather than one after the other, has probably not gained much in performance, but it's a useful improvement nevertheless, and some extra knowledge which is useful to have. This kind of code is something which will be more noticeable on a larger application.
39. Environment variables: In this video, we are going to be using what is called environment variables. This will basically be a separate configure file inside of our projects. Which lists all of our user account names, passwords and authentication details, all in one place. This has certain benefits. For example, it keeps our secret information in one place, meaning it easier to ignore this one file when sharing our projects all push into a service like GitHub. Meaning our code is in a much safer way for security. When attained up to production, many hosting providers also spots these environment variables, making setting up our hosting even easier and this is something which we'll look at later when we push our app to Heroku. Also if a reuse any of these variables, such as passport formation in multiple files, changing or leasing one configure file is a lot easier. For this, I'm going to be using a nose package called.env. This will allow us to basically create a new file with the.env extension and this will be used to store our variables inside. At the moment, the only sensitive information we have is stored inside of our app.js. Let's open this up. app.js, it's this information here, which is our database connection but we'll be adding more sensitive information in upcoming videos when we connect to Cloud Storage for images. First of all, we can import the package as usual inside the terminal. Down at the bottom we use a npm install, close down the server and run npm-i, then.env. Give us a moment to pull in a package from mpm. Let's just restart the server. Would npm run their stuffs? So now if we open up our sidebar, we can go ahead and create the Config file, which we are going to be using to store all of our variables inside. Close this down. Then in the root of the project create in a new file, which is simply.env and make sure this is in the root alongside the app.js. Then we add our variables using name, value pugs and also using the capital letters too. I'm going to use DB, which is for our database, and then satisfy DB variable to be equal to a database, URI. This is over in the app.js, open this up, then we can quote our database URI and the connection, take this out with the quotations and then paste this in as our DB variable. Behind the scenes this.env module, we'll take all the variables we create inside this file and add them to what is called the process.env objects, which is provided with node. This object contains information about the user environment such as our username and also the user directory. The variables were add inside this file, will also be added to this object too and this is how we access them inside of old files. To understand this better, we can output this process.env objects and see what it includes. If we go to the hotelController and then go to the homepage filters, which are before. Let's do any console log and see what's included. At the top of our clients section, let's add a console log, then it's logged to the console, the process.env and then go for.USER, which is capitals..USER is one of the properties on these objects and it contains the current uses name. If we save this, pull up the terminal and go to the browser, go to our homepage, it reloads, then over to Visual Studio Code. If we scroll up to the top, we now see the username of Chrisdixon, which is stored inside this use of variable. Along with many more things which are also on this objects. We can also access the present working directory with.PWD, save this and then reload the browser of the terminal. If we scroll down after the two green lines where the server has restarted, we can see the current path to the present working directory. Finally, you can also try our own DB variable which we just created. Change PWD to be DB. Reload the browser, Scroll down and we see the value of undefined down at the bottom here. This is because we need to require it first inside of our project if it work and inside of the app.js is where we're going to add this so it's available as early as possible inside of our projects. So app.js, let's go up to the very top, above all these variables and then require inside the brackets, the.env package. We don't need stall this inside of a constant as we don't need to access it again inside the file but instead, we said.config with the semicolon at the ends, which passes the contents of the env file and assigns it to the process.env objects, which we looked at before, then we can try again to reload the browser. Save this file, reload our projects. Now we see a problem inside the browser. If we go to Visual Studio Code, we can now see some error messages inside here. This is because we now try to setup our Mongoose connection, but we have a empty connection just here. So instead we can now pass in our DB variable, which you create it before. We can add the same just like we did inside the console log with process.env.DB for our variable. Save this and now if we reload the browser, this is all working again and then scroll down to the bottom of the terminal. We can now see we're all putting our database variable, which we still have in the console log just here. Now let's remove is console log as we don't need this anymore and make sure that this is still working in the browser. Good. If you still see the information from the database, such as our hotels and also the countries down at the bottom, everything is still working but by using environment variables. We will add more variables to in the next video, where we'll look at using cloud storage with Cloudinary.
40. Handling file uploads: At moments, our images for the hotels stored inside of our project folder, we saved to the database, a filename, which refers to the image in our public folder. This is fine for testing, but we also want somewhere to store our images, especially when our app is pushed to production. We don't want a admin to access our project files. To do this, I'm going to use a service called Cloudinary. Cloudinary is a Cloud storage platform which allows us to upload images and also videos. You can find this over at cloudinary.com. I'll be using the service to upload our images from the admin section when creating a new hotel. We can then retrieve this image when displaying our image in our website. Cloudinary also has lots of additional features too. Although we won't be getting into any of the advanced features of Cloudinary, there is a lot which you can do, so just cropping, scaling, and enhancing our images before delivery, along with adding different effects. But during this course we'll be keeping it as simple as possible by simply pushing and retrieving images from Cloudinary. First of all, we need head over to cloudinary.com and create a free account. Click the ''Sign Up'' button, and then add our name inside here, the e-mail address, password, and it's all optional. Image in video management and create an account. I want to click on "Developer", "NodeJS" and also will go for other click on ''Next'', and it closes down, and then we're taken straight to the Cloudinary dashboard. You will see there is some generous free upload limits and more than enough size for our project. Here we can see how many bytes we've used in our current storage. Over on the right-hand side, we can see we can get an extra file storage when sharing on social media. But we have more than enough to get started with. Up at the top of the page, we have our account details which we'll need connect to Cloudinary, including our API key and our secrets. We can copy these details and place them in our dot ENV file, which we created in the last video. First of all, let's copy the Cloudinary name. Click on this and then ''Copy''. Then over in our dot ENV file, let's start a new line and this can be our Cloudinary_name, and set this equal to our name we discovered. We also need a Cloudinary_API key. We can set this equal to our API key, which is on here. Copy and paste this in. The third one we also need is the API secret. This is also provided in the dashboard so Cloudinary API secret. We can go to the dashboard. The API secret is hidden by default so click on reveal, copy this, add this in, and give that a save. Then we need to install Cloudinary NPM package into our project. To do this down in the terminal, close down our server, control and C. Then we the command NPM, I , cloudinary. Remember, I is just the shortcut for install and give some moments to pull it in from NPM. It looks like we got a spelling mistake. [inaudible]. Lets go to the package dot JSON and check this is installed. You have Cloudinary as a dependency. We can now go over to our hotel controller so close this down, go to our hotel controller in the controllers folder. We will be accessing Cloudinary using our hotel controller so we need to require it in this JavaScript file. Up at the top sets up a constant of Cloudinary. This is equal to require, and this is a no package so we just simply reference this by its name, semicolon at the end. Then we can set up our Cloudinary config object, which will store all the conflict details which we have placed in our dots ENV file. Just in here, we access our Cloudinary variable dot config. This is a object which you pass in. First of all, we need to set the cloud_name, this is equal to our Cloudinary_name which we saved in the dot ENV file. Remember we can access these variables with process.env. Then in comes the letters or name of Cloudinary. Let's go and name. The next one we need, which is separated by a comma, is our API_key. Again, most of this to our environment variable so process.env Cloudinary_API_key, and the last one we need is the API secret. This is the process.env on the last one was the Cloudinary_API_secret. When uploading images, it's not straightforward as handling the other parts of our form data, such as the text fields for the name and description. If we go over to the hotel_form, which is a mixin. Open up the mixins folder. Open up hotel_form. Currently when saving our hotel, we use the default encoding, which handles all of our text-based inputs, which you haven't here. Currently we have a file inputs for the image. We have the inputs with the type of file, but present we are not really saving an image. All we're doing is saving a file name, which there matches a file inside of our public folder. When saving images in a form using a post request, which we are here, we need to change the encoding of the form to be what's called multi-part/form data. Just after our method, we can add the Ink type and "multi-part/form-data". This will allow our image file to also be included with our post request. However, we now need to add some middleware to our express application, which knows how to handle these multi-part/form-data. For this, I'll be using a package called Multer. Multer will basically take our images which we upload and then allow us to do something with them. It can save the images to insert in file location or even some memory, which is what we'll be doing in this project. We can then push these images to Cloudinary. The first thing we need to do is install the NPM package down in the terminal. NPM, I and then Multer , which is M-U-L-T-E-R. We'll also be using this in the hotel controller, so back over to this, click on this file. We can also require this at the very top. Comes multer equals require multer semicolon at the end and as we said before, multer gives us full control over where we store these images. We can go ahead and set this up now. Just our config. We say storage constants and set this equal to multer dot disc storage. Then passing AMC objects. Inside of disk storage, we can set an options object to setup a folder or destination where you want to save these images to. I will not be doing this because I'm going to be saving these images to Cloudinary. Therefore, we don't need to setup a storage folder in this case, if we just leave these options object empty, like we see here, the computer's default directory for saving temporary files is used instead. Then multer itself also takes in options object two. Inside here we're going to pass in our storage variable, which was created. Let's just turn a multer where our files will be stores. We already have this covered with the storage variable just here. We can also pass in file filters to restrict the accepted file types and also any upload limits too. But I'm just going to keep it like this to keep things nice and simple. We can then save this inside of a constant, so we can access this soon. I'm going to call this constant the upload and set this our multer object. The next stage after this is to tell multer we only want to accept single files. We do this by accessing our upload variable, which was created, and then dot single, so this tells multer, we only want to upload one single image at a time. Multer also has options too for handling an array of files. You can go over and check out the documentation. If this is something which interests you. Inside a single, we pass in a name which I'm going to call image inside the quotations as passing image inside here. The single image will be available to us since multer will add it to the request dot file object. This image name which you pass in just here, is a field name used when passing the image to request dot file. Handling this image will be a free stage process or free stages of middleware. The first stage which is to upload the image and save to the memory we have just handled above. To add this to our middleware, we first need to export it. Just for our upload we can now export dot upload. Set this to our upload dot single. Then add this to our router in the middleware chain. Let's go to the index dot JS inside of our routes. Let's locate the admin routes which we have here. We want to add this when adding a new hotel. We need to go to the post request for admin/add. Here we currently have a piece of middleware called create hotel post. This is the final stage, which is saving it to the database. But first we can pass in a file uploader and so add this onto a separate line. Then just follow this, we can access the hotel controller dot upload separated by a comma. Now this is our first and last step in our set up. In the next video, we're going to finish things off by creating a new piece of middleware to save our images to Cloudinary before pushing our hotel to the database.
41. Saving images to the cloud: We made some good progress in the last video, we set up our cloud nary config file, we handled the multi-part slash form data from our form and also sets up the molto middleware to handle saving the image. In this video, we're going to be handling the next stage, which is to take this image file we uploaded and then push it to cloud nary. Also handling any errors along the way. This will also be setup as a piece of middleware. So over to the index.js file. This needs to be placed between the image upload, which I did in the last video and then the final stage on the database. Make sure as a comma just on the end here and then we can add the hotel controller. Then the next stage, which is to push to Cloudinary add a comma at the end. This post request when we add a new hotel, will pass through these three pieces middleware in order. First of all, uploading the image, then saving to cloudinary and then moving on to saving the hotel to the database. Over in the hotel controllers dot js, we can create push to Cloudinary. Just under our exports dot upload, we can add exports towards push the Cloudinary equals our requests responds. Then next set up our function body. The first thing we need to do is to check if there is an image within any statement. If request dot file. Multiply says this file on the request objects under the name of file. Here we check if this file exists because we only want to push the file to Cloudinary, if the image is actually there. There is a case when the image is not there. A nice when updating a hotel. The admin majors wants to be obtained in the description or one of the text-based inputs. Then it leaving the file upload empty. Then after this we can add a L statements. This is going to pass this to next, which will then move on to the next piece of middleware if no file exists. This next piece of middleware is the create hotel post, as we said in the routers index file. Inside of the if block, we access Cloudinary's upload er. Then use Cloudinary dot upload method. This upload method, we'll upload an image which we pass in. We can access this use need request dot file dot path. Then we can use a promised a handle what happens when the upload is successful. Just after this, we can remove the semicolon. We can then chain on a dot, then. Just make sure this is spelled right. Dot then to create our promise. Then inside here we're going to create a function which takes in the results. Then set up our function inside here. This result variable which we have here will stall in the image which we get back from Cloudinary, which also includes a public ID, which Cloudinary adds to the image. We will then use this public ID to reference the image in our projects by setting its value to request dot body dot image. Request dot body dot image is equal to our results, which we got back. Then is public underscore ID, which is returned from Cloudinary. This request dot body dot image is impulse to create hotel post, which is the next stage in the middleware. This image ID isn't saved when we save the hotel to our Mongo database. To move this to our create hotel post, we need to call next. Add this just afterwards. This will move on to create hotel post, which is the last stage in our middleware, which you just here. As we already know we need to finish off a promise by adding a catch statements to handle any errors and redirect to the same page we are. Just after the dot then we can train on the end day dot catch. This is also a function where we can add a res dot redirect. This is going to redirect onto the current page, so forward slash admin slash add gives us save. Now we can go to the browser and add a new hotel. Down in some no wanting to run NPM or DEF starts. It looks like we have an error so let's pull this up. It's multi dot disk storage. I have a constant storage equals multer which match is just here and disc storage and that just needs V8K so save that and then grow green again, go to our project at the browser, it reloads. Now we're good to go again. Over to our admin, forward slash admin, sounds that Boltzmann, we can add a new hotel and to our form. Let's just add a test. We'll say test image. Wish I had any description inside there. Click on any hotel. Star rating it doesn't really matter. Then click on confirm to save this hotel. This is a good sign we'll now redirect it to the hotel which we just created. But we see that no image is displaying at the top. Let's go into the developer tools and see what is happening. Right-click inspects, Scroll down, click on our image. This image name is now the public ID, which I mentioned before. This is because we set the request dot body dot image just here to be equal to our public ID, which is returned from Cloudinary. Let's just plug this back down off the Cloudinary though, we should now see the image which you just added. Close this down over to Cloudinary and hit reload. Click on the Media Library. As well as sample videos and images which you provided, we also see our hotel one image is now being uploaded to Cloudinary. Excellent, so I hope this is not working for you to. In the next video, we'll also sets up our hotels to pull in the images from Cloudinary and then display them in our projects.
42. Retrieving images from the cloud: We are at good stage now as we can save our hotels and also the image is saved to Cloudinary. We also set the image name to be equal to the unique id, which is set by Cloudinary when we upload an image. Therefore, we need to change our image source Tantalus so they display correctly from Cloudinary and not just using the images from the public folder. So we can go to our Project, go to Inspect, click on our image just here. This is the file path which we need to amend. So this can successfully display in our project. This is a pretty easy effects. First we can grab the URL from our Cloudinary dashboard. So go to our dashboard just here, click on the link. Then to grab our URL, we need to click on the More link just here, and we have a base delivery URL. So click on the dropdown, and here we're given two different samples, so let's copy the URL, which includes our cloud name in the middle here. This links to our accounts, then over to our hotel.pug, which is the mixin. I'm just go to Hotel, let's open this up. We can connect this line out and keep it in for reference, but we need to create a new image elements. This time leads at the source, and we can add these as backticks, so we can use a variable. Then paste in our URL. The last step is to replace this sample.jpg at the end with our unique image name, which is stored as hotel.image. So we can you see the template literals, open up the braces, and then hotel.image, outside the braces. We also need to add the.jpg extension. If that save, now if we go over to our project, click on the "Home Page" and then click on "See All". If we scroll down, as expected only our hotel test works for now. Since the image use for this was uploaded to Cloudinary but now the rest of the image is broken because these are saved as the hotel name. Now let's delete any test hotels we may have in the database and then we need to go through the rest of the hotels and update each image. This is as simple as going out to the update hotel section of the admin, re-uploading the same image. This will then push it to Cloudinary and get a unique id, but there is one thing we need to do first. Can you think what that may be? When creating a new hotel, we passed through the upload and push the Cloudinary middleware first, which is openindex.js. We run through these two stages before saving these to the database. We also need to run through these same two stages when updates into. So copy the upload, pushToCloudinary, and then go down to admin, hotelId update which is the post request just here, once that is on a new line. Then just above we can pass through the first two pieces of middleware when updating two, since this is also handling the same image uploader, so let's test this out, so over in the admin. In fact we can remove our test hotels, so refresh the mLab and sing in. Let's scroll through any of these, and this is the test here. Click on the "Delete" icon there. We also have test image. That should be able now down to 12 images. Back over to the project, let's head over to the Admin section. We also need to now go to the Updates, so Edit, Remove, and then scroll down. The first one is Hotel1, so let's do a search for this. Click on "Update Hotel", and then all we need to do is click on the "Image", select "Hotel1", and then Confirm. Now we can see we now redirected it to the hotel detail view, which now has the image. If we just go to /all, we should see that the rest of the images still don't display because we need to go through each one of these and re-upload the image. I'm now going to leave you with this task of going through the rest of the hotels and updating images, then comeback in the next video, where we'll get to work on our hotel search form.
43. Hotel search form part 1: Up at the top of our project, we created a hotel search form some time ago. Let's get this working in it to allow users to be more specific about their booking. We'll not be implementing a live booking system but we will still use the dates to add to the customers order. This form is located inside of our layout dot pug file. If you open up the sidebar, the layout dot pug, and then we have our form inside the search nav wrapper just here. At the moment we have our form element just here but it has no action or method attributes. Let's go ahead and add these now. Open up the brackets and at the action which is going to be equal to forward slash results and then the method and that it can to be a POST request. When we submit the form, we're going to be submitting this two forward slash results. Now we need to handle this route inside of the index dot js file. The routes folder named index.js. Let's close this down. Let's add this in. Let's put this just after the countrys. On the next line here, router, lowercase, and this is a POST request. [inaudible] submitting the form data. Inside here we needs add the router of forward slash results. It's match on form. The hotel controller dot and this function is going to be called the search results semicolon at the end. Let us go over to the hotel controller and create the search results inside here. Down at the very bottom, exports dot search results. This is going to be a sync function, request object, the response, and also next, then create our function body. Inside here we're going to begin with our familiar try and catch block. Catch pass in the era calibrates and dynamic capacities onto next. Also taken in the era. We need to capture the contents of our post request and then install it inside of constants so we can use this inside of the function. Inside the try block, creates the constants, and I'm going to call mine the search query and this is equal to the request dot body. Request dot body stores the information passed from or post request i.e. Only information which is inside of the form. The search is going to be run through the aggregation pipeline. Each step will take our search data and then narrow down the results. Let's begin by setting up our aggregation and store this inside of a constant called search data. Capital D set is to await. Our hotel model.Thoughts, aggregates, just like we've used before. Then inside of the brackets we can pass in these square brackets as an array so we can pass in our various stages of the aggregation pipeline. The initial aggregation stage is going to be the match stage inside the curly braces. $ symbol match, just like we used before and then we're going to use something which we haven't seen this yet, which is $ symbol texts and is he going to be used to create a text search on our hotel fields. Inside of here, open up the curly braces again, $ symbol search. We want to much [inaudible] records to the text entered by the user. This can be achieved using this $ symbol text, which performs a tech search. Here we're passing in the search, which is going to be a string, which Mongo uses to query our database. Boosts which string do we want to use. Well, this is the destination from all form. If we go back to our layout dot pug file, the destination is the first input just here, which has this name of destination and this is also passed with the post request so it can access it with search query dot destination. Let's add this in now, so search query, which is our constant just here, dot destination. Before we go any further let's see what data is returned back to us. We can do this if we go outside of our aggregation, was the next line and do a res.json passing in our search data. This is the data which should come back from MongoDB and it's stored inside this variable just here. You just save and then over to the homepage. We can create a search. I didn't any details inside here doesn't really matter. Guests and and hit Search. Now we see if we scroll down to the bottom there is an error of text index is required. This means we need to index which fields we want to search. I'm going to make the hotel name and also the country's field searchable by this form input. A user could search for the destination, just hear much [inaudible] hotel name or by adding a country. We indexes fields over in our hotel model. Let's go over to the sidebar. Inside of our models let's open up the hotel dot js. and right down at the very bottom outside of our objects, we first begin by accessing our hotel schema, which is the name of the constant. just at the top here. Back down to the bottom. Dot index will announce the index the fields we want to search from our model. Pass in an object which takes in the fields. The first field we want to search, since we're looking for hotels. If we go over to mLab, inside here, we want to search for the hotel names. We have the hotel name field and we also want to search by country too. We can use the country field in addition to this. Inside of our schema, we can add in these two fields. The first one is hotel underscore name and this is going to be set to a string of text. Then we could do the same with the country, cities to text too , hit that safe. We can also add any of the fields here too such as the description. Unsearch would also look in his field too. If We also head over to mLab once again, it reload. When its sign in let's sign without details. Click on our database name on the top. If we scroll down as well as our hotel collection we have inside here, we can now see system dot indexes, which has two different documents. If we open this up, it may look a little confusing at first, but soon we expand this we see a reference to a hotel name and also to our country so these fields both set-up to be included in our search. Now if we go over to the project and then reload the browser, and let's do a new search. Add our country, our duration and the date is fine. It search. But now it gets our JSON data back, which matches our two hotels in the country which we added inside the form. There is one small problem now at the moment, if we then go back and do something such as a search for hotel one, hotel one inside here. Remember by indexing these two fields, we also search in the hotel name as well as the country. This should work. If we search though, you see a full list of all hotels inside of our database rather than just tell us one. This result is because we're not searching for the full hotel one as a phrase. Instead, what happens by default is it will return a matches for an individual word so we'll see all results for the word hotel and also anything matching the number one too because all of our hotels have the word hotel in it. This is why we see all of these hotels from a database returned. To fix this, we can perform a match on the full phrase. Only hotel one would be returned for research by using what's called escapes double-quotes, which looks like this. Go over to our controller. The first thing we need to do is surround these inside the brackets because we're going to be using dynamic data. Then we can use the $ symbol and surround this in the curly braces just like we've done previously. The next thing we need to do is to add our escapes double-quotes. Just after the first bracket, we use a backslash and then a double quotes and then we do the same and just after this curly brace here with the backslash and then the double quotes. The double quotes will treat this as a string. All of the words inside of our search query will be searched for as a full phrase rather than individual words which we've seen before. We also need to escape these quotes using the back slashes. These quotes are also rendered to with the text from O variable. Let Mongo know the full phrase is to be searched for rather than the individual words. If we now save this and then refresh the browser, continue over to the homepage. You can now search for hotel one. Click on search. Now inside the he would see hotel 10, 12, 11 and one. We now only see the hotels which match the phrase of hotel one. We see hotel number 10 because it starts with hotel one, save hotel 12. This matches the first part of the phrase number 11, and also our hotel one. All those behavior may not look quite right since we've named our hotels using numbers. This type of search would be more effective when these hotels have more realistic names. Meaning that the user would not need to type out the full hotel name. They may only remember part of the hotel name, but will still get the results they need. Let's go back and try country, surrounded hotel one. Let's type in Mexico. Click search and now see Mexico here and also just to get to. Good this first field is not working. Onto the next ones. Next is also a much phase in aggregation pipeline where we filter our unavailable hotels. Add a comma just after the first match. We can setup our second match just like we did above with tala symbol match. Open up the curly braces. Then inside here we can check if the available fields is set to a Boolean value of true. Now let's go over to mLab and set one of these hotels to be unavailable. Lets go for hotel number three, click on edit. The availability to be false. Down at the bottom, available equal to false. Save and go back. Now of course, the project's hit the back button and then change our destination to be simply hotel it search. Now we have hotel number three, settlement available. Now if we scroll down, we should see all hotels except number three. Keep going right down to the very bottom. Great. We don't see any hotel three returned with our results.
44. Hotel search form part 2: Next I'm going to add a filter to only return the hotel's greater than the star rating which we searched for. First of all, if you do a search, once again for Mexico, hit "Search". These hotels have different star ratings. We can check if our filter is working. Inside of the match stage. We can add more than one filter. Let's go over to a hotel controller, and then just after available of true, we can also add a second match stage, and this time we want to search for the star rating. It's star_rating as the name of the field the colon, and then open up the curly braces. Inside here once you see the greater than operator, which is $ symbol GTE. We're checking if the star rating is greater than the search query dot stars. Now we should only get results return back to us from Mongo, whichever star rating, greater than what has been entered by the user. Now if we save this and then go over to the browser, hit the "Back Button", and then change it to be a minimum of four. Hit "Search". We should see only one hotel returns, but instead it looks like we're getting an error. Let's take a little step back and find out what's going on here. First of all, if we go back to our controller, and then inside here, we can change the res.json to be the search query constants, therefore can see what data is coming from our phone. Changes to be search query, hit "Save" and then reload. Obviously here is the form data being sent from the post request. If look a little closer, this is where the problem lies. All of these JSON values are surrounded in quotations, just like the stars, and also the sort order, meaning these will be treated as a string. But the star rating in our database, if we go to "mLab" scroll down, this is stored as a number without any quotations, and this is why we don't get the expected results. We can confirm this, let's first reinstate the search data. In our res.json, satisfies our walls, comment this line out, and then we can do res.send, and we want to send is the typeof search query, dot stars, the semicolon at the end. This will then return back towards the data type of search query dot stars. Save this and then reload the projects. Click "Continue" and now we'll see we get a data type of string. Although we were expecting a value of a number for the number of stars. This is because when sending data to a from a server, it's been sent as plain text. Therefore, the client is interpreting this as a string. This means we first need to convert this data type for it to work, and we have a Javascript method called parse Int. To do this, if we go to the top of the try block just after our search query, let's create a new constant called parsed stars and set this to our Javascript method of parse Int. Inside the brackets, we can pass in the string which wants to pass, and we want to pass the search query dot stars. Copy this and paste this in. This will lend them pass the string as a number using the parse Int method, and then store it inside this constant just here. We can then use this constant inside of our aggregation pipeline. Copy parsed stars, and then rather than using the search query dot star from before, which we now know is a string. We can now paste this in, hit "Save". Then we can do our res.json by uncommenting out this line and then delete our res.send hit "Save", reload the browser, and then we can go backwards to all form. Now search for a minimum of three stars. Hit "Search" we'll go with two Mexico hotels from before, go backwards and then click on "Four star" this time, Hit "Search". Now we only get the single hotel returns, which is these star rating of four. The last step in this pipeline is to get this select box work in here to solve the price from high to low or low to high. This we can add a source stage of the aggregation pipeline. Let's go back over to our controller. After the match stage we can add a comma, and then just below this, let's create our sort stage with a $ symbol and then sort, then we can add in the field name of cost per night. We won't need to be sorted by the cost per night field, which holds the prize for the hotel. This needs then taking the sort field from the form which is stored in the search query dot sort but you may have already anticipated a problem with this. Just like the star rating, this is also a number two. We will need to use the parse Int method again on this field. The sort field is a number because if we go over to the layout dot bug and then scroll down to a Select, which is just here. We have a value of either one or negative one. The value of one will return the documents in ascending order, and negative one will return in descending order. Let's begin by parsing our number. To the hotel controller, just after the parsed stars, we can add a semicolon, setup a constant, this time called parsed sort. That is equal to our parse Int method passing in this time the search query, and then dot sort, add a semicolon at the end, and now we can use this parsed sort constants in place of the search query docile from before, give this a save, over to the browser. Let's drop down the star rating to be number one. We get our two Mexico hotel returns and then change it to be high to low. Hit "Search". Now have a warning over here so let's go back, and it just wants to be a capital I, hit "Save" and then "Reload". There's our two Mexico hotels. The cost is 67, then 56. Such high to low, but I'll go back and change from low to high, hit "Search" and now have 56 followed by 67. This appears to be working fine if we go back and change to a different destination. The Dominican Republic also has multiple hotels. Click on "Search", we see our country. The cost is in order, go back and change it from high to low. Now these also sorted in numerical order. We now have the data back we need from the database, next up, we'll move on to creating a Search Results template to display these hotels inside of our projects.
45. Search results template: Now we have the correct JSON data, now return back from the database. We need to now create a template to display this data to the user. First if we go over to our text editor, we can create a new page template called search_results. So open up the Sidebar, and then go down to our Views, New file, so this is the search_results.pug. Then we can replace the res.json from the controller to render out this page template. So let's go to our controller, which is hotel controller.js, go to the search results function and rather than having this res.json, let's comment this out, double res.render. We can render out our new page templates of search results, comma, and then personal options object with the title of search results. Let's add a semicolon at the end there. Then we need to pass to the template two things. First is the search query, which contains our departure date and also the number of nights. This is stored inside the req.body, which is the data sent from the form. So add a comma; and then add our search query; and then add a comma, and then we can also add our search data, which is the data from the database after our aggregation filters you see here. Now we can go over to our search results template. So save this file, go to the search_results.pug. This is going to extend layouts. We can include our mixins. So mixins/_hotel, because we'll be reusing this mixin to display all the hotels as a list of our search results. After this we'll add our block contents, our h2 for the title, and then we need to loop through each one of the hotels in this search data, which is being passed to us. So we'll do a loop where each call this individual hotel, the hotel. So in search data, we can add a hotel_wrapper or the CSS, add all mixin, so plus hotel. First of all, we're going to pass to the hotel. Of course we need to pass the hotel data, which is this one just here but we can also pass in the full search query too. Remember we also send search query to this template in the controller, which is just here and this is going to hold all the information from our form, such as the departure dates and the number of nights. This is all information can also be included in the search results. So let's test this out. If we go over to the browser, and then let's go back and create a new search or in fact we can just click on the search button here, and use the existing data, and scroll down. Okay, good. So now we have our search results just here and now instead of the JSON format, we now have our two hotels which look a lot nicer. This is good, but there are some more details to add to this hotel. Before we cluster the hotel mixin the search query data, which contained the departure dates and the number of nights from the user search. This is looking good, but there are some more details to add to this hotel. We want to also include over on the right-hand side the departure dates and also the number of nights, as some additional information. So we pass this over to the hotel mixin with the search query data. Let's go over to our mixin. The search results, let's get this data from here. So now we're passing two different arguments, we need to also include this inside of our mixin. So _hotel, we can pass this in as our second argument just up here and then to display the search query data inside all hotel, we can do this with some conditional rendering. Let's scroll down. Underneath the cost per night, create a new section. If we indent this back one level, so it's level with the hotel info. Create a if statements, so if url === '/results', then we can add in the other details. Let's add a new surrounding div of the CSS, of hotel order details or with underscores. Then we could just set up some p elements, to display this data. We use the back ticks, so mix the dynamic data with the text. Number of nights, a colon then the capacity in our dynamic data of searchQuery.duration. A second set of p elements, again with back ticks. This is for the departure dates, with a colon and this time this one is the search query. date of departure. This is all camel case. Next up, we need to add the totals. First of all, if we just save this and check this is working. If we reload the browser, resubmit the form. We now have a new div on the right-hand side, which has our data, which had been passed to the hotel from our search form. Now we need to also add two more pieces of information. The first one is the price per person, and the second at the bottom, we're also going to include a total cost for all of the people combined. To do this the way we'll work out the cost per person is to multiply the number of nights by the cost per night. Then we also need to create a second calculation, which is this cost per person multiplied by the number of guests. To makes this more simple, let's go over to our templates. Now we can add some JavaScript. Let's add the constant for the costEach this is going to be equal searchQuery.duration. Then we've got to multiply the duration by the hotel.cost per night. So this one was cost_per_night, I'll make this small so it fits on one line. So remember, we have the duration stored in the search query, which is passing the form. But the cost per night is stored in the hotel, which we getting from this available just here. Now let's create a second constant. This is going to be for the total cost for the booking, so total cost. So this one is going to be equal to our cost per person, which is cost each and then when we need to multiply this by our searchQuery.numberOfGuests. Now we can use these two constants and I'll put them inside of our p elements. Open up the mark ticks. The first one is going to be total per person and let's add a currency symbol. Then we can do our dynamic data inside of the template literals. Cost each at a horizontal rule, so we can have the total cost at the very bottom, inside of a h3. The back ticks for the total cost again, the currency symbol then our dynamic data, which is the variable of total cost. Give that the save and then let's try this out, we resubmit the form. Okay, good. We've got the number of nights, the departure dates, the total for one person, and then the total cost which is this multiplied by the number of guests. Let's just double-check, if we go back here we had two different guest says the total is twice the cost per person. We've also sorted the price from high to low. Let's search again. We have a higher price here then we see at the bottom. Let's go back and change this to be low to high. We can also change the guest before, and also change this. Click on "Search", go down to the bottom. Okay, great. Now all our details have been updated. We have a new total cost, which is multiplied by four. We have a new number of nights and also the price is now from low to high. If we go over to the homepage, click on the "Logo" scroll down to all hotels. We don't see any of these hotel detail on the right-hand side because of this conditional rendering. Let's try to see all, we still don't see anything in here. This is all looking good now. Remember we surrounded this new section inside of a div, called hotel order details. Let's copy this, and then we can style this inside of the CSS. Let's go over to the sidebar in the Public folder, Style sheets; and then the Style.css. Then scroll down to the bottom, outside of a media query. Let's work with the small screen first, base in the hotel order details. Let's begin by adding a background color. Keep div a little bit separate from the rest. Once you use a hex value of eed, set the text aling on the mobile view to be in the center. Then a little padding keep it away from the edge of 1em. Now we save this, and then it reload the browser on the smaller screen, add a search query. Any detail is fine. Let's search. Now scroll down. We now see the search results for this particular country. This is the new div which added with the different background color. We can see the text is centered on this new div but it's not centered on the hotel info section above on this particular folds/results page. Let's go ahead and fix this now. Again, outside of the media query, this is the hotel info section. Scroll up, so let's add the text line to be sensor. Gives us a reload, and there we go so that looks much better. Let's just quickly check this doesn't mess up any of the areas of our sites. If we go to the homepage and also on See All, we'll need to reinstate this in the media query. Let's just copy this hotel info section, scroll down to the media query, and here we go. In fact, we can just change the text line to be back to the left, reload and this is looking much better now. Back to our search query you can add a couple more styles for the larger view. Let's add the same search query, click "Search". Now on the larger screen, we still have this text on the right-hand side to be centered. This is okay for the small screen view but for the larger view, we want to align this text back to the left. Inside the media query once more, let's go down, and we can add this just after the hotel info. The surrounding div again was hotel_order_details and all we need to do is add the text line to be on the left. Save this and then reload, confirm the form, and there we go. This looks much better on the larger screen. I should shrink this down and check everything still looks fine. Excellent, the search results are now working as expected and we've also added a little styling. Next up, we'll stick with the subject of the search form by also including more on the hotel detail page.
46. Hotel detail search from: We now have a working search form in the header section. I'm now going to add a similar one to the hotel detail view. You may be wondering why we need to do this. Let's take a quick look. If we go to the homepage and then go to the hotel detail view for any of these hotels. Click on the title to be then taken it to the full view. Imagine we were a user visiting this site, and then we'd like to look at this hotel and then clicked on it. At the moment, there's no way to personalize this booking by adding our details which has the departure date, the number of guests, and also the duration. If we were visiting this hotel and we wanted to make a booking, the only way to do this would be to go up to the top navigation and create a new search. This may not be a huge problem at the moment because we don't have a lot of hotels, but on a larger project with thousands of hotels, this may become an issue for the user. What we're going to do in this video is to create a similar search form to the one inside the header. We'll go to then place it down at the bottom, but this is only going to have certain information. We only need the duration, the departure date, and also the number of guests. We're also going to remove the star rating and the price because we don't need these since we already have the hotel selected, which we want. Let's go over to our layout.pug and if we scroll down, this is the search form from the top navigation. If we copy this section from the section of div, all the way down to our submit button, let's copy this and then head over to our underscore hotel, which is the mixin. I'm going to add this form in just under the cost per night. After here we can add some conditional rendering to only display this on the current routes, which starts with a forward slash all, and then forward slash. If url.startswith, then add another string which is forward slash all and then forward slash at the end. After this, we can then paste in our search form from before. If we scroll out, make sure this is all indented correctly, otherwise we'll get some issues. The search now we can indent the section all in one level. Scroll down to the button, indent this in. At one level inside from the statements, I'm also going to separate this section with a horizontal line with the hr elements and then also add a h3 title of search for this hotel. Good. We already know which hotel we want to be searching for. We can add in the value. If we go down to the destination. Just on here, we can remove required. We can set the value to be equal to hotel, which is the information passed to the mixin, hotel and then dot hotel underscore name. Now the value of this field will now be pre-populated with hotel name. Therefore, the user doesn't need to do a specific search for this hotel. We also need the duration, the departure date, the number of guests. We don't need the star rating since we already have the hotel. We can also remove the sorting order and leaving the search. Now if we give this a save and then go to the browser, reload on this hotel detail view, scroll to the bottom, and now we have our search form down here. We can see we already have hotel seven already inside, which we got from adding the value as an attribute inside here. This hotel also much is the current one which we're on. Let just go back to the homepage and select a different one and check this is still working. Scroll to the bottom, and now hotel four is now inside of our inputs. Now we've selected the hotel we want. Let's try and personalize this so we can go ahead and place an order. Add a duration, a departure date, number of guests, click on search. Remember this form is already set up to go to forward slash results, which is the same route as the form inside of our header. A lot of this should be already handled follows. We're going to forward slash results. Then we see an error at the bottom saying the ordering must be ascending or descending. The reason we're getting this error is because if we trace it back, we're currently going to forward slash results, so forward slash results inside of our layouts, inside of our routes go to index dot JS forward slash results goes to this hotel controller of search results. If we trace this back, hotel controller, and then go to search results. We still trying to submit a query to our database. Also include in a star rating, and sort these by ascending or descending order. We still need to add some information to make sure that this is bypassed on this page. The way we can do this is just about the top here. We have our two constants which store in this data. I'm going to use the operator to add in a value if one is submitted. The two pipes for all the value of one, and then the same below or one. Basically if we use in the header form at the top, we're going to receive the number of stars and also a value for the sort order. If none of these is received we then just going to add in a value of one, to each one of these. Therefore, this shouldn't cause any problems when searching our database. Now if we save this and then reload, we now get research results as expected. This time though, only returning the current hotel which the user has selected, but also passing in the details on the right-hand side. Let's try one more. If we go to all hotels. Let's scroll down, if you go to hotel two, we have a form at the bottom, with a value of hotel two. Let's try five, add any date inside here, two guests and scroll down, and this is all now working correctly. Now let's move on to the next section where we'll step things up a little, and you will learn all about adding user accounts and authentication.
47. Creating the user model: Welcome back to this new section. Here you'll be learning all about user accounts, login in, registering new users, authentication, and so much more. To begin, just like when we started creating hotels, we need to use Mongoose to create a model, but this time for the user. Let's go over to Visual Studio, open the sidebar, and then inside of our models folder, we can go ahead and create a new file, this one is called user.js, and then close this down. We need to construct our model just like we did with the hotel. First of all, we need to add a constant of mongoose and then require the mongoose package, leave a semicolon at the end. If you remember from the hotel's, inside of the hotel.js, we created our hotel schema, set it to a new mongoose schema, and then constructed all of our fields and then added the data types and the various things inside of each one. We're going to get the same with this user.js. Let's start by creating our constants, this time this one is called the userSchema. This is equal again to a new mongoose dot Schema, capital S. Inside here we'll pass in our object. So our user is going to be pretty straightforward. It's going to have a first name, surname, an email, a password, and then an additional field at the end col is admin. Will come to this later on though, the first one is for the first name. So first underscore name, and then setup our structure inside of the curly braces. The name is going to be the type of string separated by a comma. We also need to make this field required, then the text in this field is missing, which is, first name is required, add a comma. We can trim any whitespace off by setting trim to be true, and a maximum number of characters to be further separated by a comma. We can do the same for the surname, so let's add surname. That's our second field. Open up the curly braces. It's still going to be pretty much the same so this will be the type of string. The required fields. It's like this time is, surname is required, trim again to be equal to true, and also a maximum number of characters to be 30. So there's our first name, our surname, next, open it to also capture the e-mail address. The e-mail address is going to also be the type of string. This will be required too with the text of email address is required, add a comma. We can also trim this, so trim to be true. We can also set this field to be unique. By setting unique to be true, this will make sure that we only have the same email address stored once inside of our database. We can also make sure that this is stored in lowercase by setting lowercase to be true. Add a comma after the email, and this is the password. The password needs to be the type of string two. It's also required, so password is required. We'll also add a new option to soon when we come back to encrypting our passwords, but for now we can just leave this data as it is, and then move on to the final one which is admin. Open this up. So admin, this is going to be a Boolean field. The type of Boolean, and also by default we want to set this to be false. This field is going to be used to add a admin user to our database. At the moment, we're going to set up any new user to not be an admin. Therefore, we set need default value to be false, and the only way to add this is by going inside the database and changing this to be true. We can then export our model down at the bottom, add a semicolon, and then module dot exports equals Mongoose dot model, the name of user, and then pass in our userSchema, which we declared up at the top just here. We just add off with a semicolon at the ends. Give them a save. Now this is our schema completed for now. As we already know, this model does not do anything by itself, so in the next video is on to creating a sign-up form so the user can't register.
48. Sign Up Form: We now have our use model now. On to creating a sign-up form to register a user. Earlier in the project if we go over to the layout.pug, up to the top in the header section, we added a sign-up link, which links to /sign-up. Now we can handle this route in the index.js file. Let's go over to here. Route, index.js. Close this down. If we scroll right down to the bottom, so just above the module.exports, we can create a comment of user routes. First of all, we can add router.get, since this is a get request, we can add the route of /sign-up. Then we're going to create a userController in just a moment. Then the function name of signUpGet, semicolon at the end. We're using this userController this time rather than the hotelController, which we've used so far in this course. This is because I'm going to be adding a userController, to keep things organized and separated. This index.js file will also need access to the userController too. Therefore, we can add it at the top under our hotelController. Scroll right up, here we require the controllers and so far we only have the hotelController, so just below we can add a constant of userController. Just like above, we can add the require, add path. This is going to be../controllers/userController, and add a semicolon at the end. Now we need to go ahead and create this userController file inside of our controllers folder. Open the sidebar, click on "Controllers" and create a New File. UserController, capital C.js. The 1st thing that this file needs is access to the user model. We can require this at the top inside of a constant. Constant User, capital U equals require, then add file path which is the string../ it's in the models folder /user. Below this, we need to create the signUpGet to handle displaying the sign up form. So exports, this is setup just like the hotelController, the function name of signUpGet, this is equal to our function which takes in the request, the response our function Bonnie. All we need to do is add a res.render to render a page template, which we want to call sign_up, possible options would be title of user sign up. Give that a save. Before we can go ahead and create this sign of template though, 1st I'm going to create a mixin at the Form 2. So open up the sidebar, Views, and then Mixins, create a new file _user_ form.pug. Now we can go ahead and create our form. Let's start by creating our mixin name of userForm and then setup our form as normal. The action is going to be equal to an empty string, so it makes a post request to the current rounds, so the method of post, the class, which we're going to surround each input is form_input. Indent it in one level, the label, so just like earlier when we created the form to add a new hotel, each one of these input types needs to match the name which we used in our schema, so the 1st one is going to be for the first name, the surname, the email, and so on. This label is going to be for the first name, so first_ name, underneath text of first name to appear an excellent form. The input, since it's text, we can add the input type of text, so type equals text. The name of first_name, the ID to match the label. Again, it's just first_name and also this field is required. If we go back and indent this at the same level as this form input, we can add one more.form_input. The labels begin for surname. The text of surname with a colon, so this input is also the same as above. This is going to be the type of text, the name of surname, the ID to match, which is also surname too, and also mark this field as required. Let's copy this input from above and we can reuse this for the email. The label for email, the text of email, input type of email too, and then just change the names inside of here too, the ID, and this field is also required. Let's copy these three lines from above as well for the email and paste them in, because we're going to be reusing a similar one, but this time this going to be to confirm the email. The 2nd one is going to be confirm_email. I'm going to copy this name, so the type is fine, add this in the name, add this in the ID. This field is also required and then just add the text of confirm email. The next one is going to be for the password. Let's add the form_input, the label. This is going to be the label for the password field, the input. The input, we can add a type of password and let's makes sure that the password is not visible when the user types this out inside the browser, so the name of password 2, the ID. This field is also required. That looks like everything we need for the password. Now let's copy this. Actually in fact, we need to add the text of password and then let's duplicate these three lines of code, add this in just below, and this one is going to be for confirm password. Just add to confirm and then an underscore. We can copy this. Change the text here to confirm password. The input type is also password too, so I'll paste this in as the name and also the ID. Again, this field is also required. The last thing we need now for our form is the inputs with the type of submit. This is going to have the same form_input wrapper, so the button, put in type of submit, and also a class of button small. Finally, we can finish this off with the text of confirm. Give that a save. Now with all this in place, we can add the sign-up template to add this mixin into, so into the sidebar. Now, to the views, we can create a new file, and this is going to be called sign_up.pug. This file of sign_up is the one which matches inside of our userController. This is one that we created just here, so now we need to add our basic information inside here. We go to sign-up, this extends our layout. We need to also include the mixin which we just created. Mixins folder /_user_ form, the block contents. Now onto our contents, we can add a level 2 heading, which is the title, a horizontal line below the title, and then under here we can add our mixin, which is userForm. Now all that's left to do is to try this out in the browser. So head over to the browser and let's go over to the sign up link inside the header, scroll down and here's our sign-up form which we created. I'm just going to change this to be a capital U, so we userController. I change it inside of here, reload, and that looks better. This is step 1 now completed. Next, we'll create the post request to handle signing up the user once they submit this form.
49. Validating User Input: This sign-up form which you created in the last video is only routes of forward-slash sign-up, and also the user form mixing, which we created up at the top is posting to the current URL, since we left the action as an empty string. This will be posting to the sign-up routes. Therefore, we can create a post request inside of the index.js to handle this request, so go over to the index.js and then down to our user routes, so router.post. This is also going to go to the sign-up routes, so as a string forward-slash sign-up, we'll make use again of our user controller, and this time we'll create the signUpPost, add a semicolon at the end, over to the user controller, down to the very bottom, this time it's exports.signUp and then Post. This is going to be equal to an array, so which we created a little differently to what we normally do. We will still be creating a function soon down at the bottom with the request and response, but this time I've added in an array. We're going to first include inside here some form validation functions to check the user's input for any malicious input or errors, so let's add a comment. First of all, we're going to validate the user's data. This will basically clean up the user inputs and then make it safe to pass on to our server. Form inputs, sort common way for hackers to input code, since it is a direct communication between a hacker and also the server. Therefore, we need to use validation and sanitization to clean up the input before it reaches the server. To do this, I'm going to include a node package called express validator, which we install as an npm package. Down to the terminal, close this down, so the command is npm space i, and the package is express-validator, hit Enter. This package will allow us to do two main things, validation and also sanitization. Validation is the process of making sure the user has entered values in the correct format, a bit like our models we created for the user and hotel. It can make sure username is a minimum number of characters, if the field has alphanumeric data, if a password matches, and so on. Sanitization is a process which removes, replaces characters entered into the input fields, which may be used to send malicious data to the server. Express validator comes with modules we can use for both of these tasks. If we go to the sidebar and open up on node modules, scroll down to the express validator which we just installed, so just here, open this up. Inside this folder, we're going to be using both the check and filter modules, some over in the userController.js file, we can require these at the top of this file, so scroll up, and then we can add a comment of express validator. Set up a constant, the curly braces. The first one is going to be check, which is equals to require the express-validator, which is the name of the package, forward-slash check, which is the name of the module we need to include. Remember, doing things like this, if we add the curly braces, this is used to import a single member of our module and then store it inside of a variable name called check. So this check constant will be used to validate the data. Next from the same module, we can also use the validation results, so add a comma, add validation results. This runs the validation and stores any validation errors into a result objects, but more on this soon. Next, we can also require the filter module and store it inside of a constant called sanitize. On the next line, create a constant with the variable name this time of sanitize. This is equal to require, pass in our express validator, the module name which is forward-slash filter. This module, as you may have guessed, is for sanitizing the user's data. Let's begin by validating the data using a check constant which is set up above here. Down to our array, underneath the validate data comments, we can use our check, which is the constant. Inside here we can add in our field name, which is first underscore name. These field names need to match the names which you have inside of our user form, so just here, back to the controller. The first method I'm going to check is if the length is a certain value. We do this by passing in a options objects, where we can check if the minimum length is equal, I'm going to set this to one. So isLength is a method which is being provided by the express validator, as will be all the rest of the ones, which we're going to add in now. We can also chain onto the end as many of these as we like. The next one I'm going to add with message. Inside here we can pass in a message if the length doesn't meet the minimum value which you pass in. This is going to be a text string of 'First name must be specified', and then onto the next line. I'm also going to chain to something else onto the end. We could keep going across the same line, but I'm going to add this on the next line, just so it's more clear. This time we're going to check if the text is alphanumeric with dot isAlphanumeric, the brackets just afterwards, and then we can also chain on a message just like we did above, so with message. Then pass in a string which you're going to display to the user if the result is not alphanumeric. Add a text string of 'First name must be alphanumeric'. This is the end of our first check on the first name field. We can add a comma just afterwards. But also pretty much going to do the same for the surname. Let's copy these two lines, add these in below. The only difference is the field name. Check this time the surname and then also we can change the text. The surname must be specified and also the surname must be alphanumeric. Again, make sure that the comma is on the end, so you see check. This time we're going to select the email field from our form. We can chain onto the end of this dot is email. This is a method which we provided write with express valid data which will check if this field corresponds to a email format. If it doesn't, we're also going to add the message. With message, and this is going to be a text string of invalid email address. Just like the ones above, we need to add a comma just afterwards. We can also check the next field, which is the confirm email. This also needs to be a string. Confirm underscore email. This is going to be a little bit more complex. We need to now check if the emails match and also if the passwords match too. Students, we can use a custom validator. Let's first set up the check and add our empty custom validator function just for now. We've got our field name selected. Let's chain onto the end the custom validator, with dot custom and then we can also chain on dot with message and we'll come back to this one in just a moment. Inside a custom and we can pass in a function. The first parameter is the value which it receives from the field. Add the brackets inside here, the first value, which is coming from our form. This is going to be coming from our confirm email field. The second value is going to be inside of an object. Add an options object where we can pass in multiple values into here. But we only need to access to the request object, which we can also pass in. Now this gives us all the information we need. We have the value of confirm email, and we also have the request object, which has stored inside all of the other fields. We can now finish off our function by comparing the two fields. Just after the brackets, we can set to obey ES6 arrow function to check if the value, which is our first field. This is the data coming from confirm email. Check if this is equal to the request, which is the request object passed in dot body dot email. Here we're grabbing the original email field, which is this one just here. Then we're checking it's the same value which is stored in value as this field just here. All that's left now to do is to pass in a message which is a string of email addresses do not match, with a comma at the end. The final two fields I have for the password and also for confirm password. We can do pretty much the same as this email. First of all, we'll do our first check for the initial password fields, check the password. We're going to check if this is a minimum length. Let's go for six characters. Passing our objects, minimum of six. On the next line, we can use dot with message. Invalid password. Passwords must be a minimum of six characters. Make sure the comma is at the end of this line. So now we can also confirm the password using the custom validator. Just like the email above, Let's copy the custom validator and paste this just below the password. Inside here, all we need to do is change a couple of fields. The first one is to be confirm password, to grab this field. This is also going to be stored inside of this value as the first argument. We also need to pass in the request just like before. Let's get all the information from all the rest of these fields. We then check if the value, which is this field just here of confirm, is an equal to request or body dot password, which is this field just above. We can then change the text. All passwords do not match. Make sure the comma is on the end again. After our validation, we can again set our function, as usual, passing in it the request and response object along with next. Let's set this up as an ES6 arrow function, passing in the request, the response, and also next. Up at the top of this file, we created a constant called validation results just inside of here, which we can now add inside of our function taking in the request object. Scroll down to our function, add in the validation results. Pass in the request object. This extracts any validation errors from the request objects. We can then store them in a constant to make use of. At the start, we can store a constant with a name of errors and set this to our validation results. Just make this a comma just on here. Now I want to set up an if statement to check if we have any errors. Passing this errors constants and then after that, we can add dot is empty. Then the brackets just afterwards and make sure this is spelled correctly. Dot is empty is a validation result method which we can use to check if there were any errors. Currently, we checking if the errors array is empty, but we want to do the opposite and check if there are any errors. We can do the opposite by adding a exclamation mark just in front. This section is going to handle any errors. There are errors. Then afterwards we can add an else section. This section is going to be four no errors. Inside of the if section, if there are any errors, we can add a res dot render to again show the sign of form and also a new title of please fix the following errors. Res dot render, we can redisplay the same sign-up form. Sign underscore up. Then add a comma, pass in our title inside of the object. The title of please fix the following errors, then a semicolon at the end. Let's go over to the browser and test this out. If we save this file, reload our sign-up route. Seem to have an error. Oh, we need to restart the server. [inaudible] start, reload our sign-up route. First of all, let's test this out with some valid data. Let's add anything inside of here which means the minimum number of characters, and also the date format. We can add anything inside here. Click on Confirm. We don't see anything happening at present because all this data is valid. We've not set anything else up inside of this else block to handle what to do next. We also see the same title of user sign-up, which means we haven't been redirected to this sign-up with the different title. Now let's stop this and we can add in some invalid data. Let's reduce the password to be below six characters. We can change it to not look like an email, click Confirm. The web browser is stopping us from doing that because this is set to an email field. Let's just stick with the password for now. Click on Confirm. Now we get redirected to the same pole slash sign-up route, but this time we using the page title or please fix the following errors. We've not passed the errors to this template just yet. But this is a good sign, meaning we're now detecting if there are any errors and then rendering our sign-up template with a different page title.
50. Passing errors to the template and sanitization: Now, with the errors being detected, we want to display them on the screen. To do this we can pass the errors which you have stored inside this local variable to our template. We can do this just after our title. Add a comma, we can pass the name of errors. This name of errors will be available inside the templates and this is going to be equal to errors dot array, this is array method. We'll get the full set of errors as an array, then will have access to all these errors in our template, using the error name which we gave just here. Now, we can display these errors as an unordered list inside the templates. Back over to our signup dot prog file. Under the horizontal line, we can say if errors, this code will only run if any errors are being passed to the templates. Let's create an unordered list. Then we can loop through each one of the errors of the array. For error, in errors, we're going to create a new list of item, which will be equal to the error and we can grab the error message with dot MSG. Now, we can give this a save and then test if everything is working. Save this and then reload. I've misspelled [inaudible] , error in errors. Over to the controller. Just there, give us a reload again. This is our error messages from before when we set the passwords to be under six characters long. We now get a message of invalid passwords. Passwords must be a minimum of six characters. Let's go back. Let's add small errors so we can make sure the e-mail addresses don't match. Let's also add our passwords which don't much to and also under six characters long and confirm. Good, our e-mail addresses don't match. We have an invalid password and also our passwords don't much either. These are the error messages which we set inside our validation in the user controller. These are all warns, which are just here. A lot of these messages may never even show up, if we have the required field set on old form. If you just innovate useful layer of validation, over in the browser, once you get an error messages, all of these fields which we typed in and now empty. We ideally want to keep the details the user has entered but we'll fix assume. Next we're going to move on to sanitization. This will be a little simpler than the validation. Let's go over to our controller. Back to signup post. Let's go down. First of all, let's comment out the rest or render. Then we can replace this with a red dot JSON passing in the request, the body, which is the details from the form. If we save this and then fill out the form, go back and then resubmit the form. Then we type a password in, let's go for testing. Confirm. We now, see the JSON data which has been submitted from the form. If we click on the back button and then add some HTML inside any of these fields. Open up the angle brackets all these in, Click on thumb. Password again. Confirm. This HTML is also send with the JSON, which you can see inside the first name, meaning this would also be sent to a web server to. Add in code inside of a form input like this could be used maliciously by hackers, to send potentially dangerous inputs to the server. As a rule, anytime we receive inputs from the user, we should always validate and sanitize it too. We've taken care of the validation. Now, on to the sanitization. First of all, we can make use of the sanitized variable which we created earlier, when important, the filter module. just here we install this inside of the sanitize variable. Let's go down just before all function. We can access this we had sanitize. We can again target each individual field like we did before with the validation or we can pass in a style which targets all of the fields. Then chain onto the end the trim method, dot trim. This will remove any whitespace from before and after the text fields. Finally, we can also chain onto the ends, dot escape and a comma. This will remove any HTML characters which could potentially be used by hackers to create a cross-site scripting attack. If we save this and then go back over to the browser, and reload. We see we have now, removed our HTML tags and replaced it with the corresponding HTML entity codes. We've now taken care of validating and sanitizing the user's inputs. We can now move on to the next video, which handles saving this information to our database.
51. Registering new users: We have now validated and sanitized the users inputs. Now the next stage is to actually save this data into the database. To help with this, I'm going to use a piece of authentication middle-ware called passports. Passport is available at Passport js.org. If we go over here and then click on the Documentation. Over on the left-hand side here we see many different ways which we can use Passport to authenticate our users in different ways, such as using Facebook, Twitter, and many more. All these methods are called strategies, under this project and what we use an e-mail and password combination to sign-in. If we click on Username & Password up here. Whenever it's taken to this username and password section and we can see by this functionality, this is stored inside of a module called Passport hyperlocal. We'll go ahead and install assume. In addition to this, there is also a convenient package we're going to be using called Passport-Local Mongoose. This is a plugin or extension to Mongoose, which makes it really easy to use passports, username and password combination to login. It gives us a simple register method which will take any user and password and then register this user with Mongoose. First, we need to install all these packages over in Visual Studio, so close down the server, run NPMI. Then I want to include three different packages. The first one is passports and you can include multiple packages on the same installation. Separate by space, we can also add Passports-Local, which was the strategy which you've seen before inside the passport documentation. Then the third and final of what we need is going to be Passports-Local-Mongoose. Hit "Enter". Let these all pull-in from MPM, go to number three, start the server with MPM run DEF start. In the user.js, which is inside of our models. Let's open this up. This will see schema which we setup for the user. We can require the Passports-Local-Mongoose package. Put on top of this file. Let's set up our constant called Passports-Local-Mongoose. This equal to require. This is going to require our node module, which is Passport-Local-Mongoose, since this is a Mongoose plug in, we need to add this plugin to our schema before we can use it. Down at the very bottom on the our users schema. With all the module we've got exports, we can target our User schema.plug-in and the plugin which you want to add is Passports-Local-Mongoose. This is the constant name which was set up at the top here. Next I'm going to pass in a Options objects. Separated by comma, open up the curly braces, and then we can add in the username field and set this equal to or e-mail. This object can take in multiple options. You can check these out in the documentation if you want to know more. What exactly is this username field? As we know when it's passed in, two things to login. We need an e-mail address and also a password. Signing this username field to e-mail is saying that we want to use this e-mail field from the schema above as our user name to login. By default, if we leave out this username field, it will go ahead and look in our schema for a field called username, which we don't have. Next, we need to settle Passport over in the app.js file/ head over to app.js. Double click on this and let's makes some more space. Up at the very top of this file, let's go underneath the router, will setup a comments. This is for passports.js. We need to set up passport.js inside of this file by requiring the user schema and also the Passport module. So const-user. This is going to require that we use a model. This is./ the model's folder. Then forward /the user. Next up we can also import our passport module and store it inside of a constant called Passport. Require a node module called Passport. In our express up, we need to first initialize Passport by using the passport.initialize middle-ware. Blow this, we can scroll down and you can add this pretty much anywhere. Once on this just after the view engine setup, let's add a comment. So configure Passport middle-ware. Again, we're going to make use of app.use, which we've used in the past. Go to, pass in the possible variable.initialize; then a second one, app.use. This one is passport.session; This first line will initialize passport to use inside of our application. Then the second one, by adding passport.session, will allow us to use sessions later on, skip the current user logged in. But we'll come back to this later on. We said earlier that using a username and password combination is called a passport local strategy. This can now be setup using passport.use. Just below this, we can say that passports.use, our user model U.createStrategy. These create strategy is a helper method provided by the Passports-Local-Mongoose module, which we installed, which then adds to our user schema. This is responsible for creating the Passport-Local strategy follower, to announce, to take advantage of the username and password login, which you want to use from passports. We then need to serialize and deserialize the user. First form is to type this out, and then we could talk a little bit about what this is doing. First of all, passport.serializeUser. Inside here we pass in our user objects, rubber user model. then the serialize user method. Then we do pretty much the opposite, which is passport.deserializeUser. Just like a bullet, we add destroy user model.deserializeUser. This can be a little complicated to get your head around but the main concept relates to sessions. A session which we'll look at in more detail soon, will basically allow us to store the user details, so they remain logged in when clicking from one page to the next one. These functions tell Passport how to get the information from this user objects and which information to store into our session. This is the serialized path. The deserialized path is the function that will be responsible for them getting the current users information back from our session and then back into the user objects. Therefore staying and logged in between pages. But again, we'll come back to sessions in a later video. This should be now for the configuration. Back over to the user controller. Save the user file or to use a controller. To kick things off over in the sign-up post, which is this array we created here. Make sure we have removed the res.jayson. Remove this line and then reinstate our res.render. Just after this res.render, I'm going to add in the return keyword. This return keyword will break out of the statements if there are any errors on this stage. This if section above is hunted in if there are any errors, but if they're not, we can go ahead and save the user information inside of this L section. Inside here, we can setup a constant called new user. This equal to a new user objects where we'll pass in the request of body, which is the information from the form. Then we use another method provided by Passports-Local-Mongoose, which will then register our new user. Grab I will use a model U.register. This register method takes in three arguments. First is the user which we want to register, which we will have stored in this constant just here. Again new user. The second one is a password, which is stored in request.body.password. This is just going to be a test user for now. Storing this plaintext is not a problem, but soon we will look at how we can encrypt his password before saving, which is something which we should always do. Third is a callback function. This is a function which will run when the register method has completed. Add a coma, add a function which takes in our error, let's make it a little smaller. Then create the function body. Inside of this call back. The first thing to do is to handle any errors and then pass them along our middle-ware. We'll say if an error is present that's on a console log, with the message of error while registering. Then we can add a comma and then pass in the error message, which is stored in this variable of err. After this, we can pass this along our middle-ware chain. We can return an emphasis along to next, which also takes in the error. Let's do the browser. We can create a test user and check this is working. If we go back and it goes to our sign-up routes. Lets add a test user. So test our test. Confirm. The password is fine. Click "Confirm". If we click "Submit", the browser icon up at the top will keep spinning. This is because we have not yet told it what to do next. But if we go over to mLab and then reload and I scroll down, we now see we have our hotel collection with our original told documents. Alongside this, we now have our user's collection which has more documents inside. Let's click on this. Makes more space. There's our test user which we just added with the password of testing more. As I mentioned before, password should not be stored as plain text. This is solving which will fix in the next video, where we'll look at password encryption.
52. Password encryption: We've now saved our users to the database, but it is something we still need to fix. We've saved a test user to the database, but the password is stored as plain text. This is something which we should never do. In fact, no company should ever save your password to the database like this. If the database was ever hacked, the hacker would have access to all of the usernames and passwords. There is also a good chance that a user will use the same password on other websites too. This is a big security risk. There is often a misconception that a big company, let's take Facebook for example. Could you simply go into their own database and see everyone's password, if it was registered. This isn't and also shouldn't be the case for any company. Instead, the passwords are encrypted first, and the encrypted version is stored inside of the database. To understand a little more about this, we need to be aware of hashin and salts. Hashin is basically a way of scrambling our password. When we register as a user the scrumble version is then stored inside of the database. Each time we then tried to log in, our password, will enter is also scrambled again using the exact same algorithm as when we registered and this crumbled version will be checked against the same scrambled version inside the database. This hashin should be a one-way process and there should be no way of converting it back to the original password. Hackers can use what's called a brute force attack. However, this is when they use a computer to generate thousands of hashes from passwords until one of them matches. To try and make things even more secure, we can also solve the password. Salting is when we add some data, often randomly generated for each password. The salt is added to the user's password and then hashed to make it more secure. This salt is often also stored in the database too along with the hashed value. We're going to use a package called bcrypts, which will take care of this hashin and salting follows. This is the package page which run now. If we also go over to mongoose bcrypts, which is also a npm package. To make things even easier, we also are going to be using this mongoose bcrpt package along with the bcrypt module. This is a mongoose plugin. Just like mongoose plugin we used for passports to make integrating with Mongoose really easy. First let's go over to Visual Studio Code and installed the packages which we need. Down to the terminal, npm i and then mongoose-bcrypts. Let's install for just a moment. Certain soap is fairly straightforward. We add these in the user.jsmodel. We need to require this at the top. Go into the user.js and then after our two requires at the top, let's set up a new constant called mongoose bcrypts. We set this as camelcase and so this is equal to require. We're going to require the module name, which is mongoose-bcrypts. Then we add this as a mongoose plugin, just like the passport local mongoose module which added earlier. Scroll down to the bottom where we add our plugins above our login from before, we can also access the user schema. Lets plug-in. The plugin which we want to add this time is mongoose bcrypts, which is our variable name. Then once we have this plug-in set up, we can now add the bcrypt option to our schema password field. We do this by setting the bcrypt option to be true. So up to our password. Add a comma after required, we can add the bcrypt option and set this to be true. Gives us a save and then we can go over to the sign-up link in the navbar, and we can create a new test user of the browser. In fact we need to run npm devstart. It's the browser and I'm going to go over to mLab and deletes our test user and then go to sign up in the top navbar, then let's add a test user once small. You can add anything inside here. Hit "Confirm." Then over to mLab, hit "Reload." Let's see if our test user is now inside of there, which is. Remember when we confirm when we clicked on the sign-up button down here, we've not yet settled where to go. The browser will stay on this page. Back over to mLab. Now if you expand our view, test user we can now see under the password fields we have this encrypted version now stored inside of our database rather than the plain text from before. Now we have this working. Let's delete all of our users from mLab. Delete this one and any of you users which you may have. We will leave so clear for when we move on to the next video where we start handling the login feature.
53. Logging in: Of course, having user accounts inside rotates varies, is now use unless the registered user can also login when returning. This is what we now gone to handle in this video. Also, we're don't handle login in the user immediately after registering, which is a nice little touch. We already have a login button inside of the header, which links to the routes of forward slash login. Lets begin by handling this route inside of the index.js file. Let's head out today inside of the routes folder, and then go down to our user routes, which is just here. This is going to be a Get request, so routed or Gats. The path of forward slash login. Let's use our use controller, and then create a login gets or we use controller, we can now set up this function inside here. Inside the controller, the use of controller, and then it down to the bottom bring this a little more exports.login gets equals our function which takes in the request and the response objects. Or function body, which is just going to simply rest or render the template, which is called login. Then options pass in the title of login to continue. So we go at the end. Of course, we also need to create this login at template too. We'll do that now over in the sidebar. The routes, sorry, the views. Click on the "New file icon", and then login.org. Let's begin by extending our layouts. Block contents. Level two heading with the title, which passed to our templates. Horizontal line to separate the title from the rest of our form. Let's begin by creating our form now, and decisions have the action which is going to be forward/login. The method is going to be a post requests. We're going to surround each one of our form inputs with the class of forming boots.foam_ input and says for our CSS later. This is going to be a simple form input. It's going to have the email in one group, the password, and then a submit button down at the bottom. First let's create our e-mail. Label for e-mail, and then the text for the label of e-mail two. This input is going to have the type of email. This will provide some browser validation if the e-mail type is not correct. Input type, we also need the name of email 2 and also the ID of email so match your label. The second one, and in fact, we'll just copy this form input-group vicinities below. This one is going to be for the password. Also the text off password with the and just afterwards is also a input type of password 2 the name, and finally the ID 2 of password. The last thing we need to add down at the bottom is our final form inputs, and this is for our submit button. A button, this needs to have the type of submit. Submits a form, we can also add our CSS class of put in small. Keep the styling consistent, and then the text of login. Given our final I save now and bacause the browser, which now see if we refresh on this login routes, which now see our form down at the bottom. Once this form is submitted, we need to handle the post request to the same route. Over in the index.js, we can handle this now. Let's duplicates this line here. This time is wants to be router dot post. We're going to be sending this post request to the same login routes, but only to do is change our function name to login post. Or we could use a controller. We first need to require the Passport module, since we'll be using the offense K method provided by this module right up to the top, let's setup our constant of passwords capital P. We are going to require the Passport module, at the end. Now on to login post, which I'm going to add right down at the bottom. Exports.login post is going to be equal to passports, which is our variable for the password module, and then adults of advocates. We set this login post b equal to pass boards, then Callie authenticate method, which provided with this module. The first arguments we need to pass in is the local strategy to handle the login request. Pass a in local inside here, separated by a comma. The second one is an object containing some options. Open up the curly braces and I'm going to add this onto its own line. Here I'm going to pass in two options. These will redirect the user when a login has been successful or unsuccessful. The first one is a redirect when the login has been successful, and we do this with success. Redirects, which is recognized by passports, and then will redirect the user to forward slash, which is our home route, separated by a,. We can also add failure redirect to. We're going to redirect to forward slash login, which will keep the user on the same login page. You should now be everything we need to login, we deleted only test users early on from mLab. Now, let's register a new user. Save this file over to the browser, we need to first sign of a new user. Let's go for our own user. Details inside here. A confirm O2 mLab, give us a refresh, and are used is now registered so let's try to login. If we go back to user sign-up, back to homepage. First of all, let's click on the Login option from the navigation or I'm not a typo to see if we redirected to this forward/login routes. Scroll down our email and inside here, and which comes out a incorrect password, hit "Login". We're still redirected to this forward slash login because there was an error with our password. Let's try a successful login this time and see for redirected it to this homepage. Let's give this one logo. Login with the correct details, and good. We now redirected to the homepage. Excellent. We now have the login facility working. We are relying on the redirect, so tell us if there is an arrow at the minute, but we will add some flash messages later on to give the user some feedback. The final step for this is to automatically log in the user after registering. This is pretty simple to do since we already have the login post already setup. We can just add these to the sign-up post routes. Over to our index.js, we have this login post facility just here so let's copy this. Then we need to go to the post request when signing up. Then after the signer posts, we can add a comma, and we'll add this on its own line stuff to here. We can also add login post. Once the post request is being sent to the sign-up routes, which is here, it will then go to the Assign a post and then immediately log in with this function that just here. Give this file a save, and then so expressed knows to move on to the slogan post, we need to call next in side of the sign-up post. To use a controller, let's find our sign a post, which is just here. If we scroll down, we have this if sections check if there is any areas. Just after him and we can also call next and disregards moves on to the login post. Now, I have this, this is added in the else section, which means there were no errors. Inside of this block just here, and we can now test this by adding a test user, so save this. Go to sign up, and then it's odd out test user. Click "Confirm", and then scroll down. We see we have a area down at the bottom of duplicate key index. Now, this may not be completely apparent what is going on here. This is basically down to some old indexes which we already have inside of our database. If we go over to mLab and take a look, if we click on the users, and then open this up, we have our user just here, which we setup. If we then click on the indexes, we now see we have a conflicting e-mail and also username index. We only want to go off the e-mail, so let's lead this old username from earlier. Click on this, deletes. Now back to sign-up, and let's try and resubmit the form. Good. Now redirected straight after the sign-up. If we go to mLab, click on our database. We now have our two users, which is also the Test 1 which we just added from before. It now looks like our user is logged in, I'm also getting the success redirect to the homepage. Good or users can now log in, and next we'll look at login outs.
54. Logging out: Since we have most things now setup with Passport, logging out is a straightforward feature to now implement. Passport gives us a logout function on the request object. To terminate the login session, the first thing we need to do is add a logout option inside of the nerve. At the moment we only have the sign-up and login buttons. So over in here, layout a pug file. We can also add a logout link too. Down to our views, and then layout.pug. Scroll down. We can create a new list item after login, so li, which is also going to be a link with a href of /logout and also the logout text too. Later on, we'll make sure only one of the login or logout links are displaying in the browser, depending on the user's logged in state. Over to the index.js, we can now handle this route too. Index.js under the user route, let's add router.get. The string of /logout, use controller, and this is going to be simply.logout. Let's go and create this now, inside of the user controller. Scroll down to the bottom. Exports.logout, create our function, request and response. Inside here we can access the logout method on the request objects, which is provided by Passport, request.logout, and then on the response objects, we're going to do a redirect to redirect the user to the homepage after logging out. This is all we need to do. Over to the browser, we can now give it a go. Save this file, reload the homepage, and in fact, we'll try logging in first. Login. We are now taken to the homepage. Also the logout facility redirects us to the homepage too. To make sure this is working, let's go onto a different routes, such as /all. Try clicking on log out, and now were redirected to the homepage. So I just want to make sure this is lowercase just to match the rest. So layout.pug, change this and save and there we go. So this all appears to be now working, but we can not be a 100 percent sure at the moments. In the upcoming videos, we're going to be providing some feedback to the user to let them know when the login or logout was successful by adding flush messages. Also, we'll be doing other things too, such as working with sessions, adding conditional rendering to the login and logout links, along with personalizing the header by displaying the user's name when logged in.
55. Working with sessions: When working with users, something which you will soon come across, it's a needs to manage a user session. Sessions are basically a way of saving an authenticated user, so they are remembered between visits for a certain duration. They can also be used for various of a uses too, but this will be the purpose of the sessions in this project. Imagine we are user and log into a website. As you already know, when we click between pages, we'll make a new request to the server. Imagine how frustrating it would be if we needed to log in for each new page we requested. This is because the server does not know the user's information. Therefore, we use a session as a kind of server-side storage for the user's information we want to retain during our visit. What happens is when a user logs in, a session is created and the user's details are stored in the database. We're going to be using our same Mongo database for this. A session ID is passed to the server for each request. Often this ID is stored in what is called a cookie. The server then checks this ID with the information it holds for this user and then returns the user's information if all is well. For this, I'm going to be using a popular npm package called express-session. This is the npm module which we all know. This middleware will allow us to setup sessions for our users and also add various options which we'll look at soon. Next up, we mentioned we also store the session data on the server side. If we go ahead and scroll down. This is quite near the bottom. We need to find our session stores. Here we go "Compatible Session Stores." We see a list of the stores which we can use with this module. Since we already use Mongo in our projects, we can use the connect-mongo package, which is listed just down here. This is the one we want to use. This package allows us to use MongoDB as our session store when using Express. First let's go ahead and install the package in our project. Down to the server and closes down. We need to add "npm i" and the package is called "Express-Session" hit "Enter." Once it is pulled in from npm, we can then require this module in the main "app.js." Open the sidebar, open up the "app.js." Then just after our router, let's add the comments of "For Sessions." Const Session is going to be equal to require the module name of "Express-Session." Semicolon at the end. Then onto our Mongo store. Down in the terminal "npm i." This was "Connect-Mongo." Let us install. We also need to require this package too, so also in the "app.js", just below our session, we can also add a consonant of "Mongo Store." I'm going to use capitals for this and require "Connect -Mongo. " After this, we can open up the brackets and then pass in the session. Semicolon. What exactly is this line of code doing when we just have it here? Well, as usual, we are grabbing the "Connect-Mongo" package and store it inside a constant called "Mongo Store." "Connect-Mongo" is returning a function. Put a normal require coal will not immediately execute the function. Instead, it will just store it inside of this constant. This is why we need to add the brackets at the end, to go ahead and execute this function, along with person in the session variable to this function. Then below, we can set up our session as "Middleware" to run for each request with "app.use" and also again passing in our session variable. Let's scroll down. Let's add this in just after our over "app.use" is, so "app.use" the "Session" with the brackets just afterwards. This session takes in a "Options Object." Let's add the curly braces and add this one to a new line. This creates a session middleware with the options which we're going to add now. All of the available options are listed in the documentation. For the first one we're going to use, which is also required, is the secret phrase. Let's add the "Secret." We can also store this inside of our ".env" file, since it's going to be some sensitive information. So first let's add "process.env.SECRET." This "Secret" is a text string of our choice, which is used to sign the session ID cookie. Next we can add the string inside of our ".env" file. Open this up. Then down at the bottom, we can add our "Secret." This can be equal to any text string of our choice. I'm just going to add "Travel Session" add an exclamation mark. Give this file a "Save" close it down. Next we need to set "Save on initialized" to be false. Add a comma after all secrets. This is "Save on initialized" and set this value to be "False." Set in this value will mean a new session is not saved the database unless this session is actually modified. This is useful for a visitor just browsing our sites and not actually needing their detail saved into a session. This will result in a lot less unnecessary saves to our database. Next up, add a comma and add the option of resave to be false. Setting this to false will ensure our session is not saved unless it's actually modified. Finally, we need to make use of our MongoDB store by using this store option with our MongoDB constants, which we set just above. This is one just here. After resave, add our store option and we'll set this to a value of new MongoStore, add the brackets just afterwards. This new store takes in the mongoose connection as an option, add the curly braces, mongoose.connection. Check this is all looking good. We have mongoose we have our session, our MongoStore. We can now set up a quick test to see this in action. You don't need to follow along with this if you don't want to. This will just be a quick demo. In fact before we do this, this needs to be mongooseConnection, the colon and there we go, that's equal to mongoose.connection, which we also used earlier when setting up our MongoDB, which is just here. Now we go ahead and set up the test over in the index.js file. So save our app.js. Open up the index.js, and then let's scroll up to the top. The homepage filters is on the homepage route. So I'm just going to comment this line out. We are going to create a new test function to test if the session is working by login the number of visits to the homepage in the session. First let's recreate our home route. So router.get the home router forward slash. Let's add a function, passing in the request and also the response. Inside here the first thing I'm going to do is create an if statement. If request.session.page_views. This express session module makes our session data available in the req.session objects. We can also make use of the page views to see how many times the page is being visited. Now we can increment this page views total on each request. So add in the req.session.page_views++. This will increment the number of page views each time there's a request to our homepage. Then we're going to display the number of visits on the screen by using a res.send. We're going to send using the backticks, some texts of number of page visits, a colon. Then we can add in our dynamic data, which is the req.session.page_views. Of course we need a else statement too, so else. This if section will handle if there is any page view stored, i.e if the user has already visited. The else session will handle if the user is visiting for the first time. If that's the case, we want the req.session.page_views to be equal to one and then we can do a res.send with the text of first visit. Now if we go to the browser on our first visit, we should see this text which we set here. Then for each additional refresh on the homepage, we should then increment the number of page views by one each time and then display this on the screen. Let's give this a go. Localhost:3000, it reload. In fact, we need to start up the development server. We have an error, so let's take a look at what this is. Over to our app.js. We just have a spelling mistake here. Save this and now go green again. We load the homepage and we have the text of first visit. Now if we click on refresh, this should now increment the number of page visits on each click. So three visits, 4, 5, 6, 7. Now over to mLab for the database. Let's refresh. Good. Now we'll see alongside our hotels and users, now we see a session collection has also been created with one document. If we click on this, we'll now see the details of the session stored inside here. The session has a unique ID at the top, and then some information about the cookie itself, such as the max age, any expiry dates which we want to sets, and also the number of page visits, which we just looked at inside the browser of seven. All of these options can be set bios if we want to do this as an option. When we go ahead and set up the session. The documentation has some examples of how to do this if you'd like to make any changes. Now we know our session is working, we can now remove the page views code and reinstates the original homepage. Over to the index.js, this code here can be removed or commented out. Then the router.get, which was the original homepage, can then be uncommented. Save this, reload the browser on the homepage and everything should now be back to normal. But this time we're using sessions.
56. Providing user feedback with flash messages: Earlier when we were logging in and out, we had to rely on a page redirect to let us know if this was a successful login or not. This video is going to improve our nodes by adding flash messages to the user, which will then pop up and the user can also click on a little X in the corner to remove them once they've been read. We waited until now to add flash messages for a good reason. This is because the flash message is stored in the session, so we needed to set this up first. I'm going to be using a nodes package called Connect Flash to provide these messages. Let's install this now. This is the GitHub page, if you want to find out some more information. But for now, I'm going to go over to Visual Studio Code, close down the server, and then run MPM. I connect flash and pulling this package from MPM. Once this it is installed, we can now go to our app.js file and then require this package up at the top. Let's go below our sessions. Is that a comments? This is for large messages. We'll go ahead and create a constant called flash and this is going to require the flash module, so this was connect hyphen flash with a semicolon and then we can add the flash as middleware to use in our application. Let's scroll down a little bit further and we can add this just after our passport lines. At the comment, this is going to the middleware, so we do this with app.use, which will take in the flash package, which we just required. Next, we can also add this flush functionality to our own middleware below. So let's go down to where we created our own middleware, which is just here, and then after this req.path, we can also add a new local. So res.locals, let's go to my local flash and set this equal to the req.dot flash and come out at the end. This makes the flash functionality available as a variable inside of all of our plot templates. Having this as a variable, is useful for conditional rendering. If there are any messages to show, we can set up flash for any of our functions. But to begin, I'm going to go to the user controllers, login post, so save this file and then go to the user controller and we need to find login post, which is just here. After the successRedirect, we can also add a successFlash, and then a string which you want to display to the user of, you are now logged in, so now will see this text string appeared on the screen, rather than having a redirect to show us that we're now logged in. Passport work with connect flash by default, so this is pretty straightforward to add. There's also a failure flash message two which we can add for this failure condition down at the bottom. Separated by a comma and add failureFlash, set this to a string, so login fails and will say, "please try again." Now we need some way of displaying these messages to the user. A convenient place to do this would be in the layout.pug file. This file contains our header which is on each page in the projects. Go to the layout.pug. The first thing I'm going to do is set two constants, the flash messages are an object containing both keys and values. Let's go for our beach image and we can add this just above this, so which is just here. Remember JavaScript needs to have the hyphen. So -const values is going to be equal to objects.values and then we're going to pars in the flash, so here we're parsing in the flash messages, which is a object and we're going to go ahead and use the dot values method, which returns an array of the object's property values. Remember, objects are made up of key value pairs, we now have the values here, so I'm going to duplicate this line and set this to be the keys. Change the constant to be keys and this is where we use the dot keys method. Again, parsing in the flash messages, the keys and the type of message which we want, and I'm going to be creating three message types. The first one is success, and this will be for things such as successful logins, also an error type for fail logins and general errors. Then the third and final type is for info. This will be for general information such as "You are now logged out," and then if in along these lines, which isn't a general success or error message, we will look at how to manually set these message type soon when we add more flash messages in the controller. Also passports adds these two for the success and failure of a login, so these three types are these keys consumer created here. Now on to the values. The values is basically the text ring, such as you ''You are now logged in" or ''Login failed, please try again." We only want to show these messages if there actually are any to show, so we can add some conditional rendering inside of our layout.pug, underneath our two constants will add an if statement. If keys.length is greater than zero, then underneath a div, which will be a container for the message, this div is going to have two classes. First is a general message class for styling of message and then a second class of message underscore and then the key name. The first type, so class is equal to how many backticks, since this is going to be dynamic data, so the first one for styling is message and then the second one of message underscore and then we can add our dynamic data of keys. This will generate a class name of message info, message success, and also message error, and we can then use these free styles inside the CSS later on to provide some different colors for each message type. We have the messages stored in this value's constants and we'll just add an S on the end actually, so values. I'll put these in a span elements. Just below our div intensity one level we can create a span which is equal to our values constants. Then a second span element with the class of close underscore btn. This is going to provide a little cross which the user can click on to then remove the flash message, so we can find hyphen X just like this or we can use the HTML entity of ampersand and then times with the semicolon at the end, so inside here we want to run a onclick event. Once the span element is being clicked, we want to run this on JavaScript which is going to remove all this section just here. We can do this by selecting the parent node. The parent nodes being this surrounding div just here, and then call in the remove method. We can add the onclick method and set this to this.parentNode, which again is this div just here and then call.remove. So this is a font displaying the message in our header section. So now let's go over to the browser and try this out. So over to your page, and we might need to restart the server. So npm run devstart. Once it's up and running, reload the browser, and then we can try logging in. So click on Login. Down to the bottom, we can add our login details. So at first we want to add a unsuccessful login, by adding a incorrect password. We now get the message of, login failed, please try again. So we can now click on this, so that it removes the message, and let's try one more. This time we'll answer the correct details, and we don't see a success message. So let's see what's going on and use a controller. We just have a spelling mistake there, so success. Reload this. Let's try logging out and then logging back in. We'll do this successfully this time, and now we get the message of, you are now logged in. Now we can again click on this to remove the message. With this working, we can now add more messages where we need them to display in the user controller file dot js. We can also add a message for logging out. So scrolling down a bit further just after the request or log out. We can access req.flush, and we want to pass in two things into here. The first one is the message type. Remember from before we said we're going to have three different message types; one for errors, one for success, and one for general information. Well, this is going to be a info type message with a string of, you are now logged out. So at this end with the semicolon at the end, that will give us a test. Make sure you're still logged in and then click log out, and we now have our message at the top here. Now on to the hotel controller, this also needs some messages too when we do things such as add in hotels and also updating. So click on the hotel controller, and the first one I'm going to go to is push To Cloudinary, which is one of the early ones which were added. So it just up here, down to the catch section, let's add request.flash. So this is going to be a error message. So the type of error, and then we can add a message back to the user telling them there was a problem when uploading the image. Send a text of, sorry, there was a problem uploading your image, please try again. Once you did that, we can now go on to create hotel post. So scroll down to create hotel post just below, and this is just here. We're going to add this in the trisection because this will be a success message to say the hotel has been created successfully. Just after the await hotel.save, request.flash, so the message type was success separated by a comma. We need to add this inside the back ticks because we're also going to display the hotel name. As our template literals, so hotel, the hotel underscore name which we have access to because of this hotel variable, and then the text of created successfully. Okay, good. The next one is in the edit remove post. So this is just below, so scroll down. This is going to be a info message to say that no matches were found. This is the admin section where we go to edit or remove a hotel. I remember first we need to pass in a hotel name or a hotel ID to then search the database. So let's scroll down. If the hotel data is greater than zero, this is the success section, but once I add this in the else section, in case no hotels could be found. Request.flash, this is a info message of no matches were found, and with just a couple more to our day now, so we'll go to update hotel post, but of course when we update the hotel. So let's go into the try section, and just before the redirect, we can add request.flash. This is going to be a success message, and using the back ticks, we can provide a message. Again using our dynamic data to pass in the hotel name, so hotel.hotel_name, updated successfully with the semicolon at the end. Then the final one I'm going to add is for delete hotel post. This is for a successful deletion of our hotel, before the redirect request flash. This is going to be a info type message with dynamic data. So open up the back ticks, so hotel ID, we can then pass in the hotel ID, which is this variable just from the top here, which we get from the request.params, has been deleted. Now give this file a save and then over to the admin of the browser. We now need to go to /admin at the new hotel. Let's add test for flash, and inside here, into our projects, the public folder, images, and let's go for the logo for now this doesn't matter, start writing, confirm. Great. Test for flash has been created successfully. This all seems to be now working. This is it now for flash messages, but we'll continue to add them as we need to during the rest of this project.
57. User conditional rendering: In this video, we're going to make some improvements to the user experience. Will be doing various things such as conditional rendering to change the nerve links depending on the user's logged in states also will be displaying the username inside the header, and protecting the admin routes. To begin, we can add some more locals to all middleware over in the app.js file and then scroll down to our customer middleware. So which is just this function just here. I'm going to add one more just like this. This is for the user. So inside here, we need a common edge on here. So say res.locals.user, is going to be equal to the request dot user and there should be semi-colons at the end. So should quickly change this. So the user is added to the request object by passport. When a user has successfully logged in. We can now use this user variable inside of templates to apply some conditional rendering. Currently if we go over to a menu, we have sign-up, have login and also log out showing at the same time. We only want to show the sign-up and login buttons when the user has not signed in. So over to our layout, a PRG, and then go to our nerve section about the top here. We are going to add some conditional rendering to only display if the user is logged in. So just below the logo on the unordered list, indented in one level, we can say if you use is false, we're going to indent in this list item for signup, and also login. These two buttons will only display if the user is not logged in. Else will show the logout button when the user is logged in. So to do this, we can also enhance the logout section in one level. Then we can add the else section which lines up with the if section just above. Also, since we have access to this use of variable which you just see here, we can also display to the menu, the user name. We just have to logout, we can ask the brackets and then use the hash two outputs, some dynamic data. So open up the curly braces. Now we can access user dot first name. So it gives us a save and trying to sounds in the browser. So reload. Currently we want to see the sign up button and the login. Let's try login in. I'm going to see what happens if we add our details in down the bottom. It Login. Great, so now we only see the logout button and also our name is now displayed next to this. Let's try clicking this and login out. We now get the flash message on here. We also now only see the sign-up and login buttons. Now on to protecting the admin routes from unauthorized uses. To do this, I'm going to create a piece of middleware to burn through where we need to check if the user is an admin. We can do this over in the use of controller.js, and down the bottom just blow our logo function. We can add exports.admin. This is going to be a function taking the request, the response. Also next, inside here we're going to perform two checks. First is to use a method called is authenticated. So I'm going to say if request dot is authenticated, then it'll curly braces just afterwards. Then second is to check if the user is an admin. Early when we created the user model in the users.js. Let's open this up. We added the is admin fields, which is a default Boolean type of false. I'm also going to use this in the, if statement to only allow free users who is set to true. So news controller, as well as checking if the user is authenticated. We can also use the double ampersand. It's also check if the condition of request.user.admin is set to true. This will be a piece of middleware, it's pass-through. So we also need to call next. So first of all, add next inside here, which will move on to the admin page. Then the return keyword after this, this return keyword will break out the function if this is true. If this condition is not met however, we can just redirect the user back to the homepage. Outside if statements, we can ask the res.redirects. Then as a string just forward slash to go back to the homepage. Now over in the index.js file, we can and this middleware to our admin routes. So index.js. If we find our admin section, these are Admin routes here. The first one we want to protect is simply forward slash Admin. So before we take them to the admin page, will also going to run through these E's Admin function, which is also in the use controller. So use controller dot is admin and separate these with a comma. Then I'm also going to add a second route 2 which will catch all of the routes beginning with a forward slash admin, and then forward slash any other route just afterwards. So I'll show you what I mean with this weird router. Dot gets to be a control rounds. So for such admin and then forward slash, we're going to use the star. So this will catch all routes which start with forward slash admin, and then any of the stuff afterwards. So then we're going to run through use controller.isAdmin before they proceed with any of these extra routes. Now over to the browser to test designs on the admirals. So both the browser and reload. We can see by all menu items that we're currently not logged in. Let's try forward/Admin. We then redirected back to the homepage. So let's try and login in. Now we log in with forward slash or /admin once again. Once again, we are redirected back to the homepage. So for both of these cases, we directed back here, meaning the authentication has failed. As you may have already guessed, this is because we set ourselves as a user and is admin is set to be false. Over in mLab. Maybe it's low back in. Yes, there's not the username and password and login. We can now select our user from mLab, and then it set admin to be true. Click on our database. We need the user's collection. Click on the Edit icon. So here we can see is admin is set to false. We can change this to be true. Click on Save and go back. This going into database is not something we want to be doing each time we want to create a new admin. But once we have one admin sets, like we do now, a good challenge for you would be to go ahead and create something from the admin screen, to be able to change the setting for adding new admin users. So now bar codes the project while we are still logged in. Let's go back to the admin routes forward/admin. Now have isAdmin set to true. We can see we no longer redirected and we'll stay on this admin routes. Now if we try clicking log out, we're then redirected back to the homepage. Remember we also added this catch all routes with the star. Let's try some different routes here. So admin, forward/ adds. We are currently taken back to the homepage. Let's try logging in, and test this again. Login as our admin. Now we can try forward/admin. Obviously, it will go to this, which is good. Once more we'll try login out. Now we're redirected back to the homepage.
58. The order model: Welcome back to this brand new section. This section is going to be all about allowing the user to place bookings and also the user accounts area too. Where the user can see any booking which they have placed. We'll also be adding this to the Admin Section too, so the admin can see any bookings which had been placed for all users. This booking or all this information will be stored in a database as a collection. Therefore, we need to create an order model to save our data. First create a new file called the order.js over inside of the models folder, so models new folder order.js. Then we can go ahead and create our model just like we have for the users and also the hotel. Let's close down a sidebar. This model will have three things. First, the user id, so we know who has placed the order. Second, the hotel_id for the hotel I want to book and third is the order details. This will be an object containing the details of the departure dates, number of flights and the number of guests. As usual, creates our constant for Mongoose package, which we need to require at the top of the file, so require mongoose, okay? Then we can create our constant for our order schema and satisfying new mongoose.Schema capital S, which takes in our objects. Our first field is the user underscore_id. This user id is going to have the type of string. It's also going to be a required field, so we set required to be equal to true, separated by a comma. Our second field is going to be for the hotel_id, which the user wants to go ahead and book. This is going to have a different type than what we've seen before. This is going to be equal to mongoose.Schema.Types with a capital T and the type is going to be the objectsId. The hotel_id is going to use this objectId as its data type. This is provided by mongoose and we'll need it for a good reason later in this section. We've stored in the hotel_id, which is on the booking but we also need to retrieve the rest of the hotel details from the database. Using this objectId rather than a normal string will allow us to properly compare this id to the one which is stored in the database later on. Makes up this field also needs to be required so as to be true. Then our third field is going to be for the order details, which is going to contain the type of objects and this is also required to be equal to true semicolon at the end. Then we can export our model with our familiar module.exports, which is equal to our mongoose.model. We pass in the name of order as the second value this takes in our variable name of order schema, which is this one, just here, so at a semicolon at the end and give this a save. This is enough for our order schema, next we'll get to work by creating a booking confirmation page and this will allow the user to review all the details before finally placing a new order.
59. Booking confirmation page: At moment we can go to any hotel which you want. Select this and then down at the bottom we can add our travel details. If wanted to book this, we need to add the duration, the departure dates, the number of guests, and then perform a search to provide all of the details. If we click the search received, this search result is now personalized with all the details which are centered. This is good, but now we need to add some more stages to allow the user to book this hotel. The first step I want to add is a Continue button to this page, which will take the user to a booking confirmation page where they can review their final details before placing the order. This hotel view is inside of the hotel mixins. Let's head over to this file in the text editor. Sidebar, mixins and then underscore hotel. Down at the bottom. Scroll to the bottom this file, we can conditionally render a button to only show on the results routes. This is the routes which are currently on and then we're going to add a Continue button just under the total price. This total cost is on here. Let's make some space and we can say if URL is equal to /results. If it is, we can provide a link with the class of button, which is going to have a href equal to and when it's on disembark ticks. This is /confirmation and then our Dynamic section, which is going to be our query and then the text all of continue for all button. For this link, we're going to be linking it to a URL of /confirmation, and then forward slash a query string, which is going to build it up from the information needed for this booking. This query string will be a series of name value pairs. This is the same method as we used earlier in the course when we use the iTunes API. Let's create this query string now above. Just above our if statements, we can create a constants called query, it's match here and this is going to be equal to a dynamic string, so add the back ticks. As we mentioned before, this is a series of name value pairs. The first one is going to be the hotel ID. Let's set the ID to be equal to hotel._ id. Remember all this information is going through, passed along the URL as a query string and then we can grab this information and use it in our controller. Then we need the ampersand to add a second name value pair. The second one is going to be for the duration. The ampersand, so duration and this is going to be equal to the search query which we've used before just above to grab our duration, the date of departure and also the number of guests. SearchQuery.duration, after the duration, this is going to be the dates of departure and remember this is just one long string and this one is going to be equal to. This is again going to be the searchQuery and just like we've used before, this is searchQuery.dateOfDeparture. After this, we can add a new ampersand, which is going to be for the number of guests. This is going to be equal to searchQuery.numberOfGuests, add a semicolon at the end and all of this information will now form our URL. Let's try this out in the browser. Save this file over to our browser and if you still scrolling on the Results page like I am. Just reload the browser, reconfirm before submission. I'm now see a Continue button at the bottom. Click on this button. We're taken to a route we have not yet handled. We got a follow full-page as expected. But if we look up to the URL, we can see the route which we created of /confirmation and then our series or name-value pairs. We have the ID equal to the hotel ID. We have the duration of seven nights, the dates, and also finding the number of guests equal to four. This is all the information we need to construct a order. As everyone known it's handle this route in the index.jsfile. Let's go over to the routes index.js and we can capture this query string inside of a variable called data. In our user routes on the log out, we can add router.get/confirmation/ and we use a colon, since this data is dynamic. This is going to be a user controller, I'm going to call the function Booking Confirmation. Then over to or we use a controller. We'll create this booking confirmation and we'll go right down to the bottom just after logout. So exports, so it's booking confirmation. This is going to be a sync function. Since we'll be working with the database, passing the request response creates a function body, the transaction, the catch block, passing in the error and also pass this error to next. Since we're using next error, we also pass this in after the request and response. The first thing I'm going to do is capture the query string from the URL. Remember, we can access this from the request.params, followed by the name we gave it in the router. In the training section we'll create a constant called data, so request.params.data. Before going any further, we can check what data we have with a res.json. So res.json, the data from our query string. If we save this and now refresh, we now have our ID, the duration, date of departure, and the number of guests. This appears to be the data of ones, but it's not being displayed in json formats. It's just a plain string. This is because we first need to pass the query string. Node provides us with a module called query string and this is a core module. We don't need to install anything extra, all we do is require it in the file in which we want to use it. Let's go back over to overuse controller, up to the top of the file and it creates a new constant called querystring. Then require the module which is called query string. Now we can pass the data constants which we created below and store the results in a new constant, which I'm going to call search data. Back down to our booking confirmation. We can go just underneath the data, create some new constants. I'll search data, which is going to be equal to our query string which we just imported. Use the.parse method, parsing in the data from above. Now we can add the search data inside the res.json and see what happens. Give this a save and then reload the browser. Great. We now have the data in the format which we can work with. Currently, we only have the hotel id stored in res.json. We can now search the database for the full hotel details using this id. Let's go back over to our booking confirmation. Below, I'll search data, we can write a constants called hotel. This is going to be equal to await our hotel model dot find and the information which we want to find is the hotel based on the underscore id. The id which you want to search for it in the database will be stored in the search data and then we can access the.id. Search data.id and makes use a H inside here, since this is our model. Remember when creating our order schema, in the last video, I said that the hotel id needed to have the data type of object id. This is why we did this in the the last video. This id will now correctly compare to a hotel id in the database because it is, if it was a string, it would not work. One of the thing too, if we go over to the browser, we see the hotel is not defined. This is because we need to import this hotel file too. Let's go up to the top. We can say it const of hotel and then we can require the hotel model, so../models folder and /hotel. Now we can go down and replace our res.json with a res.render. Back to the booking conformation. We can add rest.render. We're going to create a confirmation template so let's add this info now. Then our options objects will pass in the title of confirm your booking. We also need to pass the data to this template. We want to send to it the hotel constants, which is going to contain all the information from the hotel which had been ordered. Also this search data just here, which will contain the information such as the date of departure. Hotel and also the such data. Now we can go into our views and create this confirmation templates as a pug file. Open the Views folder and then create the confirmation.pug, close this down. This will be a fairly straightforward templates which will basically show the hotel mixin along with the hotel data and the search data, which we are going to pass to the mixin. This you go into extends layout. We're going to include from the mixin folder, the underscore hotel block contents. The level two heading, which is going to be the title. We can then create a loop with each eye in Hotel. Then as our hotel mixin at the bottom, which is going to take in I, which is each individual hotel and also the search data which will pass to this template. Remember this contains information such as the number of guests and the date of departure. This will all be needed for the confirmation page. If you let us change the conformation spelling and so rename, make sure this is correct and save this file. Over now in a hotel mixin, so underscore hotel.pug. This hotel already receives the data from when we created the results templates. We already have the hotel, which we passing it to this variable as i and the search query. Most things are already set up for us. If we save this file and then reload the browser, we see the template is showing, we have all hotel details. Down at the bottom, we have the star rating, we have the country, the cost per night and the name of the hotel. But we don't see any of the search details such as the number of nights. Let's check this out over in the mixin. If we scroll down and we take a look at the results section, this mixin has all searchQuery details we need conditionally rendered. These only shown if the routes begins with /results. We can also add to the end the confirmation routes him. If the URL is equal to results or if it's the page which we're currently, which is confirmation and then our dynamic data. We can check if the URL.startsWith. We want this one to begin with a /confirmation/. Then reload on the same page and now if we scroll down, we see this search details also appear at the bottom. We almost finished now for these templates. But there's still one small thing to add. This is supposed to be the booking confirmation page to confirm the user details are all correct before placing the order. Therefore, we need a button to actually allow the user to place the order back in the hotel mixin. We only have a continue button down at the bottom for the results page. We can also add one for the confirmation page two. Else, this becomes be an else if section, because we're going to have a final else section down at the bottom. We'll take a look at this in a later video. It's URL.startsWith, we want the rounds of /confirmation/. We're then going to render a button just like above so a.button(href=' ') and I'm just going to leave this as a empty string for now and we'll deal with this later on. Place your order and we'll continue this href section in the next video where we ask the code to place the orders. But now if we save this file and then reload on the confirmation routes, we now see a button down at the bottom for placing the order. Good, things are now moving on well. I'll see you in the next video where we'll move on to placing the orders into the database.
60. Placing orders: The next step, once the customer has reviewed the details on this confirmation page, is to be able to actually place order. In the hotel mixing we added the route we need to place the order. We go to the _hotel, and then down the bottom we added a empty route for the href. I'm now going to add this as forward slash, then order placed, and then forward slash, we can add our query inside here. Open up the curly braces and also parsing the query, which is all the data we need for the order. But just one quick thing to fix first, we only want this button to show if the user is logged in. We can use some form of conditional rendering. After the else if we can also add a if section. If the user is logged in, we then want to display this button, so indent this in one level, else we can redirect the user to the login screen. Let's add the final else section down at the bottom, else I'm going to copy this button and then paste it in, and this can redirect the user back to forward slash login, where they can go ahead and log in for placing an order. Please login to order. There is room for improvement here if you wanted a little side projects. When redirecting to this login form, we don't currently keep stored all of the search details. The user would need to login and then perform the hotel search once again. But I will leave this with you as a challenge if you'd like to go and do this. Now, over to the index.js. We can handle this order placed route. Save this file, index.js. Then we can add up a router.get, turn that around to just create it, which was order placed forward slash, and then we're going to capture the data once again from the query string. The userController.orderplaced, and the semicolon at the end. Then we create order placed over in the userController. Just after the booking confirmation, let's set this up, exports.orderPlaced. This is going to be async, request, the response, and also next. I'll try block to begin with. We can then catch any errors and then parse these errors over to next. Just like we did above for the booking confirmation, the first thing we need to do is create a constant and grab the data from the req.params. In fact, we can just copy this line, paste in the try block, and then we also need to use the query string module, which again we used before to parse the data into JSON. So let's set this up as a constant, parsedData, this is going to be equal to our query string module we imported. Well, to use the parsed method, parsing in the data from above. Now it's time to make use of our order model we created at the beginning of this section to construct a new order to push to the database. Blow our parsedData, then create our constant of order. I'm set this to a new order, which is O. Before we can use this order model O, we need to require it at the top of this file. This needs to be O, and then up at the top just stuff that our hotel and create a new constant called order. Require from the models folder, which is..slash models, and then forward slash order. This order we're constructing needs to be the same structure as the order model we created at the beginning of the section. If we go over to this order.js, this has three different fields, the user id, the hotel id, and also the order details. First, let's add in the user id into our order just here, so user_id is going to be equal to a req.user._id. Then the hotel id, hotel_id, this is going to be equal to the parsedData.id. Then finally, the order details, which has the type of objects, so order_details, then we could set this up as an object, just like we specified in the order details. This order details will contain all of our information from our query string, which is stored inside of parsed data. The first one is the duration, which is equal to parsedData.duration. The second one is the date of departure. Again, this is equal to the parsedData, and we can access this with.dateOfDeparture. The third and final one is the number of guests, which is going to be equal to parsedData.numberOfGuests. This is all the information which will make up our order, we have the hotel details and also the order details along with the user. Now all we need to do is call save on this order, so we can await the order.save, and then after the save we can also add a flash message to let the user know the order has been placed, so request.flash. This message type is going to be Info and a text string, so this is going to be, "Thank you. Your order has been placed." Then the final thing to do is to then add a reg.redirect, which redirects the user to their account, so reg.redirect to a route of my-account. This route has not yet been created, but we'll get onto this very soon. Over in the browser, and then we can go ahead and test this out. Save this file, over to the browser. Only confirmation page at the moment with the button of place your order. Now if we reload, we now see the button has been changed to, please login to order. Let's click this, and then we'll go ahead and login. Once logged in, we can now go to a hotel. Let's go down to the bottom. We can do a search. Let's go to the number of guests. Click on "Search". We're now taken to the results page which has our details, we'd continue through to the confirmation page, which again has all the details from the order. Then click on place your order. We're now taken to my account and we also get the flash message of your order has been placed. If we scroll down, we also get a follow for message. But this is nothing to worry about just yet. We'll go ahead and handle this route soon. But if we now go over to mLab and head over to our database, inside the collections, we now have a orders collection with one document. Let's open this up and see what it contains. Expand the view. Good. There is our id for the order, have the id for the user, and also for the hotel. We also have the order details for the duration, the date of departure, and also the number of guests. That's great. We can now move on to the next video where we're going to create the new user account area, where the user can see all of the bookings which they have made.
61. User account area: After placing the new order, we are redirected to this My Account section, which we've not yet setup. This is what we're going to go ahead and handle in this video. The My Account area will be a Personal section where the logged in user can see any orders which they have placed. Just before we do this, we need to also register a new user to test things are working. Let's go to log out and you can sign up as a new user. Let's just add this as tests. I'm going to test it in as the password and confirm. Good, we now logged in as the test user. Now we need to go ahead and place a new order for any hotel. Let's go for this one. Click on here. Then we can add anything we want down here to place an order. There we go. Search for this, continue till the confirmation, and then place the order. If we now go over to mLab, we should now see two records inside of the orders collection. There are two orders from two different users. This now gives us some information to work with, so we know what to do. Now we need to first go over to the index.js and then we can handle this My Account section, so index.js and lets add this also to the user routes, so router.get, my- account, this will also be handled by the user controller. Then we'll go ahead and create myAccounts, so its the user controller. We can set this up now. Just for is admin, exports.myAccounts. This is going to be async, request, response, and also next, our try section, catch any errors, and then pass this to next. When setting this up, we could go ahead and do something like what we've looked at. For example, we could go const orders. We've used this plenty of times before. The users, the orders, and also for the hotels. Select the Orders.find. Then we could use this Find method to match the user_ID to the information from the request.user._ID. Then we can do a res.json to output the orders. Over to the browser, still only in myAccount, you can hit ''Reload''. Now see the json for the results. This works fine. We see the one booking from this particular user, so we've this user ID. We also have the order details for the single order which is being placed. The small problem is we probably want to show the user the hotel details such as the name and destination inside of their accounts. But currently, we only have this hotel ID which the user wouldn't recognize. Over in mLab, we have various collections. Let's go to our database. We have the hotels, the orders, the sessions, and the users. This ID which we currently have is in the orders collection of the database, but the hotel details in the hotels collection. Basically, we need a way of grabbing the hotel details from another collection. We can do this by using the lookup stage from the aggregation pipeline of Mongo. Back over to the myAccount section, we can reuse the constant of orders. But this time we're going to write order.aggregate, so let's just remove this section here.aggregate and then plus. We still need to filter by the user. We can do this in the match stage. Open up the curly braces, $ symbol match just like we used before. Then we're going to write the user_ID matches to the request.user.ID. This is only going to grab any records where the user ID matches the current logged in user. Add a comma at the end and then we can add the lookup stage with $ symbol lookup. First, we need to specify the collection we want to get the data from. Currently, we in the orders collection, but we want to grab the data from the hotel's collection. We can do this by adding from, and then as a string, we can add in the name of our collection, which is hotels. Next is local field, which we're going to send it to the hotel_ID. Local field is the field name from our order collection, which is basically considered the inputs. Hotel ID is in our order.js file. It's this moment you see here. Add a comma at the end and then after this we can add the following fields, and set this to a string of underscore ID. This is the field of underscore ID from the hotel's collection, which we're going to match to. This hotel data we matched to will be added to our orders as an array. Now we give this array field a name of our choice. We use the as keywords and then give it a name of hotel_ data. Now, if we save this file and then go to the browser, we can refresh the json. Now we've the new field of hotel data and this is grabbing all of the hotel information from this hotel ID, but from a different collection. This means we now have access to all of the hotel data and we can use it inside of our templates. Let's go back to the controller and create this template now replacing the res.json. We can remove this line here and replace it with a rest of render to output a new template of user_accounts. After this, we pass in our options. The title of my accounts and also the order's information. Save this, and then we can create our user_account templates inside of our views with the.pug extension. Then go ahead and add the contents. Inside here we answer the familiar extends layout, block content, the level 2 heading of title and the horizontal line just to separate the title from the constant. I'm going to add an if statement to check if the user is logged in. If it is, we can then go and outputs a level 3 heading and the back ticks since this will be dynamic with the message of Hi, then a comma, then we can output the user's name by selecting user.first_name. This will say something such as, "Hi Chris," with an exclamation mark at the end. Then we're going to check if any orders are present in the database. We can say if orders.length is greater than 0. This section will only display if there are any orders. Then we'll add a level 3 heading with the text of your bookings and for all bookings. We're going to loop through all of the orders in the database for this particular user and then I'll put them inside of an unordered list. We'll loop through by using each order in orders, I'm going to surround this in a div with the class of bookings so we can add CSS later. Then we can construct our unordered list. We first list item which goes via the back ticks so the text of Order ref. Then we can add the order reference which is stored in order._id. Underneath this first list item, which is for the order id, we're then going to create a second loop which is going to loop through our hotel data. Remember the hotel data is this array which we've added onto the end. Once we are in this particular order, we also need to loop through this hotel data to access all the information inside of here. To do this we create a new loop with each and we'll say data in order.hotel_data. All we're going to do now is create five different list items which are going to be for the hotel, the country, the departure date, the number of nights, and also the number of guests. Let's add our first list item. The hotel is going to be equal to data.hotel_name. Data refers to all of the data in this hotel data section. We're then going to access the individual keys such as hotel name, the country, and the cost per night. Have the hotel name, close off the back ticks and then I was going to duplicate this four more times. The second one is for the country and then data.country. The third one is going to be the departure. The departure date is not inside this hotel data. This is part of our original information here so we can access this with all the details.dateOfDeparture. Let's remove this section, access our original order which is the first loop at the top here, so order.order_details.dateOfDeparture. Next one is for the number of nights. Let's copy this section here and then paste this in. All we need to do is change the date of departure to be the duration. Finally, the last one is going to be for the number of guests. We can remove this and replace it with the order_details.numberOfGuests. This section will appear if there are any bookings by using these if statements. If there isn't, we need to add an else section down at the bottom at the same level. This is just going to display a level 3 heading with the text of no orders to show. Then we'll say, yet... This is for all booking section. Remember at the top we checked if the user was present. We can also add an else statement for the section 2. Right at the very bottom we can add this at the same level as the if statements. Make sure these are lined up then we can add a second level 3 heading, which simply say, "Please login to view this section." Now over to the browser, remember to replace this JSON with the rest of render. Now we can just refresh the My Account area, scroll down, and this details of our test user. This booking is hotel number 6 so this is good. Let's log out and we can try our other user. Let's log in. Now we can go to My Account area, so /my-account, scroll down, and this is hotel number 2 so this is a different order than we've seen just before. We now getting the correct orders for the correct user. Just to finish things off, we can go over to the layout of profile and other linked to this My Account section. Open up the sidebar, go to the layout.pug. Just above this logout link, we can add a new list item at the same level. Add a link with the href and this is going to my-accounts with the text of my bookings/accounts. Save this, close the sidebar and then reload and now have a button and taking those to the account section. With this now working, next we'll do some work in the admin view to allow the admin to see a full list of bookings by all users.
62. Displaying all orders: In the last video, we created an account area where the user can see their own bookings. Puts us in admin, we want to be able to have all bookings available to see for all users. If we login as an admin and then go to forward slash admin, we already have this admin view available to us. Earlier when creating this page, we also added a menu item called view bookings, which you can now go ahead and use. This is over in the admin.plug file. So let's open this up, inside more views, so click on the admin.pug, and here we go, this is the view booking section which links to forward slash admin, forward slash orders. So now we need to go over to the index.js file and handle this route now. Let's copy this route, close it down, I'll close more of this files down, go to the index.js and keep this organized. I'm going to go back up to the admin section. So these are the admin routes and then add router.get, since this is a get request. And then paste in the string of admin forward slash orders, this is also going to use the user controller then we're going to create all orders inside the user controller. So you give this file a save. Lets head over to the user controller.js. So this all order section is going to be very similar to the my account section which we created in the last video. So we can copy this full section from before and then paste in just below. We need to change my accounts to be all orders. So the main difference between this and the my account section is we don't need these much stage from the aggregation pipeline. This is because we don't need too much to a particular user, we can simply look up all of the orders inside of our database. So we can remove this match stage from our order, we also need the template to be changed to a template called orders, and we'll create this soon and the title of all orders. This is all the changes we need to make. So now we can create the orders template inside of our views, we need to create orders dot pug. So to begin, we can extend the layouts, block contents, pass in the title as a level 3 heading, a horizontal line. So under this horizontal line, we want to display the orders. Remember from the last video, we already added the code to display the orders inside of this user account.pug file. So if we open up the user accounts, this is all the same code we have here to display the order. So rather than type out all this again, it would make sense to add this to a mixin. Let's copy, or in fact we'll cut only information from the list item, the number of guests, right up to this bookings if at the top. We cut this out over to the sidebar, inside the mixins we can create a new file, and this one is called underscore orders.pug. This mixin is created just like we always do with the name at the top, so mixin order. In fact we'll call this orders, which is going to take in the individual order. Then indented in one level, we can paste in the booking from before. Make sure this is all indented correctly, otherwise we get some errors. Now we just need to add this mixin into our two files, starting with the user account. We just clip this out from before. At the top we can include are mixin, which is in the mixins folder, and then underscore orders. Then scroll down we can now add our mixin. We want this to appear on the screen so we just stuffed our loop, we can add in orders. I'm going to pass in the individual order from our loop, save this file and then we can do the same for the order.pug file. So we select this, at the top we can include our mixin, so mixins forward slash _orders, then under the horizontal line we can create our loop, just like we did in the user accounts. Each order in orders, then we can add our orders mixin, which takes in the individual order from our loop. Give this file a save, back to the admin. We can now click on view bookings down at the bottom, which takes us to the relative admin forward slash orders, scroll down, and now we can see two different orders from two different users. As well as being logged in as an admin and being able to see all orders, we also have our own unique accounts section at the top. Let's click on this too, and now if we scroll down, we should see just our own honors, but we have a 404 of not found. We can see there is an issue at the top. We can link it to forward slash admin forward slash my accounts, and we don't want this admin section inside here. We just need to go to my account. Let's go over to the layout of pug, we just need a forward slash before my accounts. I should try once more, clicking on my bookings, now we just have the single booking for Chris's account, and again if we go to Admin, go down to View Bookings. Because we're admin we can also see other people's orders too. Along with this, this also means our mixin is also working fine too, since we're now seeing this order information in two separate files. This is it now for displaying all orders inside of the admin screen. In the next video we'll go back to our styling by adding some final CSS.
63. Final CSS: It's been a little while since we worked with our styling. So let's take a few moments to make some adjustments, particularly to the features we've added in the last few sections. I'm not going to add a great deal of extra CSS. You can go even further if you want to Now what's make a few small changes, starting with the buttons. So you probably already noticed if we go to a hotel, and then if we do a search for the date, number of guests, once we've taken through to the results page, we have this button just here for continue. We also have a button for placing the order. So the first thing I'm going to start with is these buttons on here. So let's go over to the style.css. So we'll go over to the public folder, which close on these down. So the public folder. Then we need to go into the style sheets and then the style.css. So inside of this file and also outside of the media query, we can add our button styling, so let's scroll down to our media query, which is just here. We can make sure we're outside of this. So just under button small, I'm going to add the button class, which is inside of our HTML. We can add a background color. I want to set minds be cadet blue, and a small border-radius, all five pixels. Also a little padding of 0.5em. Save this. Reload. Now our button looks a lot nicer. If we now go over to the account page by clicking a link at the top. These bookings list, which you've added in the last few videos, also need some work too. This was inside of the oldest mixins orders.pug file. This has a mixins called _orders. So let's go to the mixins and open this file up. This mixin was surrounded with a div with the class of bookings, which we can now use in the CSS. Sparkle to the style.css. Let's again, add these outside of the media query so target the bookings ul. I'm going to add some margin of 2em, the top and bottom, and zero to the left and right. The backgrounds, again of cadet blue. Also a small padding value of 10 pixels. So let's save this. Reload my account section. So next of all, we want to target the individual list items and set these up to be block-level, so they're stacked on top of each other. So after the bookings ul. Let's add bookings li. Set the display type to be block. The background, so this stands out from the cadet blue. I'm going to add a gray value of eee and also some padding of 0.5em. Let's see how this looks in the browser. Add an s. There we go. So I'm not going to go too deep into this styling as lot is down to personal preference. But the last thing I'm going to change is the flash messages. These are located in side of the layout.pug file. So let's head over there and scroll down to our message section, which is just here. We see a class of message being applied to the div, which will hold our general message styling. Then also a dynamic class of message underscore. This will either be info, success or error. Using these class names, we can create different colors to each message type. For example, red for an error. Also below this there is a span with the class of close button. This can also be used to style the cross, which removes the message. So onto the styling. So we again, outside of the media query, let's start with the.message. This was for our general message styles. So some margin of ten pixels on the top and bottom, zero on the left and right a small padding value of 0.5em, a border of one pixel, and a solid line, a border-radius of five pixels. Also the font size to be 1.2em. Save this. Let's try looking out. So this now looks a little bit better. Let's now target the cross to just here and move it over to the right-hand side, as well as changing the cursor to be a pointer. We will hover over. So underneath the message, we can target the close_btn. We can float this to the right. So now we can also target the close button. But this time the hover states. All I want to do here is change the cursor to be a pointer. So the next three styles which we are going to add are all going to be related to the message type. So remember from before inside the layouts, we have this dynamic section of message_. So [inaudible] _info, success and error. So we can use these to divide three different colors, follow the flash messages. So let's start with the message_info. The message_success. Then the last one is message_error. So all we're going to do here is add some colors. So the first one, I'm going to add an RGB value of 40, 92 and for the blue a value of 177, for the success, the value of 39, 87 and 39. Then for the error message, the red color is going to be an RGB value of 233, 66 and 66. Let's add a semicolon at the end of these three lines. Save this file, then reload the browser. So first of all, if we try to log out, we get a blue message for info. Let's try logging in. So first of all, if we do a incorrect password on login, we get the red error message. So let's try again with the correct password this time. Now we get the green color for success. Excellent. This is now working well. I'm going to leave it here for the CSS styling. But of course, go ahead and change things to suit your needs.
64. Preparing for production: Welcome back. This is going to be a pretty exciting section as we see all of our hard work finally paying off. We're going to finally push our project to a live web server for the rest of the world to see. First of all, a few measures I'm going to take to get our app ready. First of all, I'm going to enable compression. This will add gzip compression, how we deal with files, we compress on our computer. Which can decrease the size of the response body, which speeds up our app's performance. This is an npm package, simply called compression, which we can install via the command line as usual. Off to the project's npm i compression and then hit ENTER. It's pulling this package from npm. Once this is done, we can then require this final over in the app.js right at the top, just like we normally do, so the app.js. Let's close down some of these our new stamps. Nope, not, the app.js right by the top, I'm going to create a constant called compression and require this file. Then we need to add this compression as middleware with app.use. Scroll down and it let's close down this after our app. Let's say "Compress responses" with our app.use parsing in compression. This is all we need to do to set up compression for our responses. I must say it is noted in our response headers later on. Next is a security npm module, which is called helmet. This module will not protect our app from every security risk out there, but enabling it is a step in the right direction and it may cause a lot of common precautions which we should take. This module works as middleware and sets various HTTP headers, which we'll look at in just a moment. Let's first install this module with npm i and the package name of helmets. Once this is installed, we can then go over to our app.js and then require this module again at the top, just under our compression, so const helmet, require the helmets module. This should then be set as early in the middleware chain as possible, again, using app.use. Just after we set up our Express app, so scroll down to var app equals Express. We can have this in just here, so app.use parsing in helmet and then a semicolon at the end. Just before we get to work with this though, I'm going to comment this line out, so we can see what our headers look like before we use helmets. Keep this commented out and then go over to the developer tools. When it starts up the server with npm run DevStart. To see our HTTP headers, we can go to the developer tools inside a Chrome, so right-click and inspect. Then if we go to the Network tab, let's close this down, reload, click on the local host, and then when you click on the headers tab, which is just here. I want to bring this up so it's more visible on the screen. The information we're interested in is this response headers. This information known as a header, is information parsed with the request and the response, and let's put this on the side so it's easy to see. There we go. Great. There is our response headers, which we can see just here and just as a comparison, I'm going to take a quick screenshots of this. I'll make use of this in just a moment. It contains things such as our Content-Type of text/html, it's set to have a character set of utf-8, which is the standard. The encoding type of gzip, which was set of bios before when we nail a compression. One of the things we need to address here is the X-Powered-By tag, which is down the bottom and this is set to be Express. Helmet will hide this information along with others so hackers can't exploit any known vulnerabilities inside of the framework which we use in. It's not going to completely hide the fact we're using Express, there are other ways to check but this is a step in the right direction. Now if we go back over to the project and uncomment out, this piece of middleware, save the file. Now if we reload the browser, click on the local host and then go back to our response headers, we see we now have more header information. Let's pull up the screenshots from before, double-click on this. The first thing to notice in this new response header is we no longer have this Powered-By Express section down at the bottom, this has been removed by helmets. There is also some extra information now added. I'm not going to go too deep into everything here, but I'll give you some general information about what's going on. We can see we have the Content-Type options set to be "nosniff". This prevents the browser from trying to detect or guess the name type. This is the type of file we're dealing with, such as a PNG or a JavaScript file. This prevents the browser from automatically trying to detect the Content-Type and basically getting it wrong, therefore, running some code which it shouldn't. We also have the DNS-Prefetch-Control set to be off. This is more of a performance feature rather than security. When we visit a URL in a browser, the URL name, such as Google.com, is basically an alias for a numeric IP address. DNS is a server which contains a database of these URLs and also the IP addresses to then march them up. This URL such as Google.com, is much easier for the humans to remember than the real numeric IP address. This feature tells the browser not to make a DNS request too early before the user clicks on a link or loads resource. We also have the Download-Options to be noopen. This protects against an old internet explorer vulnerability, which allow the browser to execute downloads inside of your site's context. Setting this will stop internet explorer allowing malicious html downloads to be executed in and unsafe environment. We have the Frame-Option set to be SAMEORIGIN. This one will control if your sites can be loaded into an iframe or not. It should be disabled unless you have a good need for it. Then we have XSS-Protection and XSS stands for cross-site scripting, and is a way for attackers to have access to our websites. They do this by finding ways to run JavaScript code inside of those sites. This is one of the things we're trying to protect against when sanitizing the user's inputs into all forms and helmets also has this option to help with some basic security. As you can imagine, now security is a deep subject and check out the helmet docs for more information, if you'd like to find out a little bit more. But for now know this is some basic protection now in place. In the next video we'll finally upload our app to production by using Heroku.
65. Pushing our express app to Heroku: I'm now going to show you how to push your finished express application to a platform called Heroku. Heroku is a cloud-based platform which allows us to easily deploy our apps to production. It works with Ruby on Rails, PHP, Python, Node, and [inaudible]. There is also a free tier. So we can learn how to use it without any cost and we only need to pay if our project grows. Heroku rooms on apps called dynodes, which are basically fully managed containers. To push to Heroku, we first need to install a version control software called git. This is available from gitscm.com. So click on this link here. We're then taking it to the homepage for git. So let's go over to the download section at the bottom. Click on this, and then click on the download for your operating system. So I'm going to click on the Mac version just there, and set it off downloading. I already have git installed on my machine. So if you don't want to go ahead and follow the installation instructions to install for your particular operating system. If you are unfamiliar with git, it is an open-source version control system, which allows us to work on projects or software and then push our changes at various stages. This allows us to not only keep track of any changes, but also to go back to earlier versions, if we mess up. Git is installed on our machine, and we can also push our code to hosted services such as GitHub, or in our case we can use it to push to Heroku. So once you are ready, we need to go over to the terminal or you can use the built-in terminal in visual studio code. So inside the here at the bottom, we can check git is installed correctly, by using the git dash dash version commands. If you see a version number returned back to us, this means git has been successfully installed. Git doors are project into what is called a repository, which you can think of as a storage buckets. We can initialize an empty repository in our projects with the git in it command. I already have the repository already setup. So this reinitialize is the git repository. You might get a slightly different message, but as long as your repository has been created, we now good to move on. We can then check the status of which files in our projects are being pushed to git and which have not, by using the command called git status. So hit enter and now see a list of red files and folders from our projects. The illustrious red because they are classed as untracked. We need to declare which files we want to add to git, and which ones we don't. Before we go any further though, there is something we usually want to do first. This is to create a new file in our project called git ignore. This because we have all of these files and folders, but we don't always want them all to be shared on git. We don't want to share the.env file because this has sensitive information. Also the node modules folder can be ignored too, since it is a huge folder which will take up a lot of space. So all the design bar, in the root of our projects, what's close all this down. Create a new file called.gitignore. So to ignore these files, we can then list them inside of here. So starting with the node modules folder. So just add them by name. So the node modules folder, we can also ignore the.env file, and then finally at the end, the DS-Store. The DS-Store is a file which is often added automatically when using a Mac. So we want to acknowledge it too as it will not be needed for all projects. This git ignore file becomes even more important if we push this code to somewhere such as GitHub. If we forget to add this, our sensitive information could be online for anybody else to see. Also the node modules is not needed, because we can always run the NPM install command, to install them all from the list in the package.json file. Also inside of this package.json file, I'm going to add [inaudible] setting, and this is to declare which version of nodes we're going to be using. So if we're going down to the terminal and type in node dash-v, I'm on chromium version10.3.0 So we can set this in the package.json. So open this up, and then right down at the very bottom onto the dev dependencies, we can set the engines. This is an object where we set the version of nodes to be a string, which is our current version. So I on 10.3.0, and of course this happens to be the current version which you are using. We set the Node version inside here. So the production version of Node will match the same version we are using during development. This could avoid any potential problems when using different versions in development and production. Now if we close this down and go, in fact let me save this first, and then back over to the terminal. We can again run git status, hit enter, save the git ignore file, and then run git status, hit enter. Now we see we are now missing the.env file and also the node modules. So now we can go ahead and use the git as command, followed by the dot. So git add dot hit enter. This tells git to add or to stage all of the files which are red. We can also use git add followed by the file name to add one file or folder at the time, rather than use dot, which would use to add all of the files. If we again run git status, hit enter, the files and folders are now set to be green. This means they are now ready to be committed to git. For this there is the git commit command. So type git, commit-m and then inside of quotations you can add a message, such as initial commit. So we add this message to describe what changes we've made by using a dash m flag, then a message in quotes just afterwards. So hit enter, and this should now commits all of our files into our empty repository. Over git status, down to the bottom hit enter, will now show that it is now nothing to commit. Our work in tray is clean and it will stay this way until we modify any of the files. So for example, in the style.css, if we added something such as padding:1px, save this and [inaudible] git status. But now see the modified version of the style.css. But I'm not going to push to git, since this is going to be deleted. So this is now our work completed with git. So now we need to move on to Heroku. The first thing we need to do is head over to Heroku.com and create a free account. So let's head over and sign up. So add our details inside here. Out of a role, go for a developer, add the country, select our primary language. I want to go from node, no robots, create a free account and then you need to go into your emails and confirm the accounts. Go ahead and do that and come back in a few moments. Okay. Welcome back. Once you've confirmed your new account and set up the password, we need to proceed on, which then takes us to the dashboard area of Rocchio. Inside the dashboard area, we can create a new project with this icon at the top. Click on New, create new app. We then integrate a application name. Let's travel, which is unavailable because I've used it to before, so let's go for Let's travel to, which is available. Select the country or the region, and then create a application. We then take into our app and we can see we have this deployment methods section just here. I'm going to use Heroku git method, which you see here, which uses the Heroku CLI. Using this method, we need to install this CLI. Click on the link just here and we'll open this up in a new tab. Download and install, and then install for your particular operating system. Once it's finished, you can click on this and then go through the installer. Good. Once this is done, we can now go back over to the terminal and now we'll have access to the Heroku commands. Down in the terminal we can add Heroku login to then log into our accounts. I want to change the email to the one I just registered. Hit Enter and then add the password, link to our accounts. Great. Now we're logged in as our email usage just here. I'm going to type, Clear, to give us more space. Back over to the project dashboard, we need to now copy this line of code just here, which is Heroku Gits. This is also linked to our Let's Travel project, so copy this and paste it into the terminal and then hit "Enter." So this will set our Heroku App as a remote version of Gits. A remote branch is a version of our project which lives elsewhere, on Heroku or GitHub. This is our remote version now set and now the final stage is to go ahead and push from Gits to Heroku. We do this with Gits push Heroku. Git push, Heroku and then Master, hit Enter. The master branch is the main or the default branch of Gits. Give us a few moments to push to Heroku. Once this is done we can either copy the link we'll see inside the terminal, or we can access a command called Heroku Open. Let's build our project and push the Heroku. Good. That is all now done, so we can either copy the link we see on here, or we can type in Heroku and then open, hit Enter. It should now open up inside of our browser at the link which we've seen before. We seem to see a error inside the browser. This is because in our project we setup environment variables, but we've ignored the.ENV file, which contains them. Now we need to add these variables back into Heroku, back over to the dashboard and up to the top. We have a settings option just here and then inside here we have a section called Config Variables. Let's click on reveal the variables and we see we don't have any of these setup just yet. Now we can copy the variables over from our.ENVfile. Let's open up the. ENV folder. The first one is DB. Let's copy this. We can add our key as DB, and then copy our MongoDB value. Paste this in as the value then add. Just do the same for our cloud, and rename the value, the API key, place this in. Next we have the API secrets. This one in, and then we just need to remove that and just have the key. The secrets and then add this. Finally the secrets of travel session. Add this in, there we go. As well as these environment variables, there is one more which I'm going to add. Down at the bottom, it's going to be node_ ENV. This is going to be set to production, so add this. This will change the default node environments from development to the production. This will change some of the app settings, such as removing some error messages, which we don't want the user to see. It will now cash our CSS and review templates for performance. It will now ignore the dev dependencies from the package.json file. You no longer need these in production. Now if we go back over to the browser tab, click on this and then reload the browser. We now see the project is working again. This is now a live link which you can now take and show your friends and family. Also if you do have your own custom domain, you can use this to read Heroku and you can find out how to do this in the documentation. Play around with this and everything should still work as normal. We should still be able to log in. Let's give this a go. We're now logged in, we can go to our accounts. There's our bookings, all images still seems to work in from cloud unary, which is good. Of course everything is still being pulled in from Mongo because we've added the environment variable from mLab. Remember though the version of mLab we've been using for testing isn't designed for production, but this is fine just for learning purposes. For now little congratulations on pushing your Express Application to production. But for now though, a huge congratulations for getting this far, and also now pushing your Express Application to a live web server.
66. Thank you: Congratulations on reaching the end of this course. I hope you are now a lot more comfortable building full-stack web applications using technologies such as Node, Express, MongoDB, Hotspot, and so much more. We started at the very beginning looking at how Node and Express work, alongside building the let's travel application. We built page templates, used routing to switch between pages, and learned how all the parts of a Node and Express app fits together. Then we moved on to how we could integrate databases into all projects, including modeling our data, using various methods to get the correct data when needed, a long with the essential create, read, update, and delete actions. After this, we reinforced our knowledge by creating more of the projects, including different views, controllers, environment variables, file uploads, and Cloud storage, along with some new techniques from ES6 Onward. We covered handling user accounts, including registering and logging in, validating the user's inputs, working with sessions, flash messages, and also conditional rendering. To round off the project, we handled the user accounts area and also allowed the user to place bookings. Finally, we pushed our projects to a live server for the rest of the world to see. Bye for now and hope to see you in another course sometime soon.
67. Follow me on Skillshare!: A huge congratulations from me for reaching the end of this class. I hope you really enjoyed it and gained some knowledge from it. If you've enjoyed this class, make sure you check out the rest of my classes here on skill share. Follow me for any updates to be informed of any new classes as they become available. Thank you once again, good luck. Hopefully I'll see you again in future classes.