Transcripts
1. Welcome To The Class!: Hey, welcome to this course. This course is going to teach you everything you need to know to get started with Vue.js and also all of the new and improved features with Vue version 3. You will build these four projects while learning from over 130 clear and organized HD video lessons, each one supplied with captions and also the source code for each project stage. So you shouldn't have any problems if you get stuck. Who am I? My name is Chris and I'm a web developer from the UK. I built my first ever website over 20 years ago. The web has changed quite a lot during this time, but my passion for learning and also teaching of this still remains. I've also used Vue.js from the early days, so I've got lots of experience both using for my own websites and also with teaching of this too. I'm excited for this one, and I will see you in the class.
2. What You Will Need For This Course: To get started with this course, there is very little you will need. If you're watching this video, the chances are, you already have most of the things you need already setup. We're going to need a web browser and I'm going to be using Google Chrome throughout this course but you can use any other major browser. Also for editing the code, we will need a text editor. I'm going to be using Visual Studio Code and you can find the link just here. This is also free to use too. If there's anything we need long way during the course, we will download it. There won't be any additional purchases to make, so all you need is an eagerness to learn Vue 3. I'm going to begin this in the next video.
3. Share Your Work On Skillshare!: When taking any course, it's really important to
not get in the habit of following along just for the sake of taking
off another lecture. Take the time to
process each lesson, review the code which you write, and think about how you may 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 divert away from the class, and you can even take a step back after you've
finished the class and 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 too
here on Skillshare. Not 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 Vue.js & What Can It Do?: At it's most fundamental Vue.js is a framework for building user interfaces. Its main job is to handle a part of website application which the user sees and interacts with. It takes in some data and displays it to the user. Since Vue is responsible for the user interface, it needs some data to work with. The data which it receives can come in any form, such as from a web server, an API. We can store it inside of our application and also from the user input too. For beginners, it can be very friendly to get going with, only needing some HTML, CSS, and a little JavaScript knowledge. As soon as you get more comfortable with Vue.js and for more advanced users, you will also appreciate how fast and scalable Vue can be. Along with a mature ecosystem too which extends the Vue.js core if needed. The Vue.js core itself is very lightweight and can be used from simply controlling a single part of a website, right through to handling a complex single-page application. If you're not sure what a single-page application is just yet, there will be more on this this later in the course. At its most simple, imagine if we had an existing portfolio website, which was created with just HTML and CSS. Further down the line, we may want to add some new features or sections to this site, such as our recent tweets. We could go the long way around and manually copy over all of our tweets into our HTML file each time it creates a new post or a new tweet, or we would probably want to handle this with the Twitter API to pull in our recent posts automatically. With such a use case we could use Vue.js to control just a part of our site, or even multiple parts if we wanted to. All we need to do is to link the Vue.js CDN link, which is a link to the Vue library. The CDN link is inserted into a HTML file, just like with any other CDN link. Then we can access Vue features from a JavaScript file. We will come back to this and give this a try very soon. Along with controlling a part of the user interface, Vue can also control our full-page application too. We will use a tool in the upcoming project called the Vue CLI, which will allow us to create these applications, and as I mentioned before, we need a data source such as some products and [inaudible] them past the Vue which can then display them to the user in any way which we want, along with handling any data changes and updates along the way too.
5. Overview of Vue 3 changes: Vue.js version 3 is a major rewrite. One which came about for multiple reasons. The codebase for Vue 2 was fast, efficient on performance, but there is always room for improvement in the current design. Led by learning and discovering new things about the project, alongside the ability to now access newer JavaScript features which are available inside of modern browsers, this video is going to give you an overview of some of the key changes. Then later on when we roll a bit more Vue.JS code with a better understanding of how it works, there will be some more dedicated sections to provide us some more detail. Maybe it's not what a tomb which if some of this doesn't make sense at the moment. First of all, I want to address a big misconception about Vue 3. Since the announcement of the new version, many people think that everything they know so far about Vue is now obsolete. This is not true. You can use the Vue 3 library and still right Vue apps pretty much in the exact same way as you did in Vue 2. Many of the changes or under the hood performance improvements that the developer is not directly aware of, Vue 3 does have some features too, but these are generally additional. Meaning if you have any Vue knowledge so far, this is all fully transferable. One of the big improvements of Vue version 3 is the performance. This has been improved in multiple ways, and one of the key things is by using a ES6 feature called proxy. This results in a faster and more efficient change detection system. This is a key part in the Vue 3 reactivity, which is a process of Vue reacting to changes and updating where required. For example, if a user updated an item quantity in a shopping cart, we would also want the total price to be updated too. This is the process of reacting to a change. In Vue 2, any data which we had when we upload is automatically reactive, may miss some data changes, and there is another part of the application which relies on it. That part will also be updated too. If the user change their username, and this was displayed in the header section, it would update their automatic too. one of the caveats from Vue 2 reactivity system was if a new property was added to Vue.JS after the application had loaded, it would then not be reactive. This is something which proxies solved and we'll look at these more detailed later. Reducing all of the size of the code can also improve things too. When we use a framework, we often don't need all of the features which it provides. Meaning that when a application loads, we may be downloading additional JavaScript or assets which we don't need for our project. To help with this, Vue 3 is more modular. So more modern tooling can dropouts any unneeded code, but using the bundle size. Another big improvement is a rewrite of the virtual DOM. Again, there is a dedicated video later once we get more comfortable with Vue.JS, but for now the virtual dom update means that you can now more efficiently check which elements have changed after an update. The Vue 2 library was wrote in regular JavaScript. Over time, it was realized that a project of this size would benefit from introducing types, which will help reduce a risk in bugs. Even though the Vue 3 cause being rewritten using typescript, developers can write code even in regular JavaScript, or the now fully supported typescript 2. Typescript was available in Vue 2 puts much more deeply integrated into Vue 3 and also requiring no additional [inaudible]. The composition API is probably the most talked about feature in Vue version 3, and also the one feature which has caused the most misunderstanding about how Vue 3 will force us to write apps in a completely new way. As mentioned before, the competition API is not intended to replace everything we know about Vue so far. It's intended to be purely additive and gives us a way to make our project more maintainable and reusable as they grow. We will dive into this a lot deeper later on. In addition to this, we also have some Vue features to explore, such as portals, fragments, suspense, and so much more. The key takeaway is Vue is still Vue regardless if it's version 2 or 3, but version 3 has some nice performance improvements along with some additional features you can use. Now have a little background, let's move on to writing some Vue code.
6. Project Source Code: When learning a new skill and coding our four projects, there are probably will be a point when you will get stuck. Sometimes your code just won't work, and you can't spot that error. Now to help with this provided with the course after each one of the lectures is the source code at each individual stage, and you can download this from GitHub. Inside of GitHub and my profile, which is chrisdixon161, and then /vue-3-course. Here Here where you'll find all the project source code for this course. It should be pretty easy to navigate. At the top, we have the first four projects numbered 1-4, and these are in the same order which you will take them during the course. For example, if you stuck on the portfolio project, click into this, and then we'll see in order all of the lectures which will go through for these projects. They are numbered, so we have number 2, which is the section number and video 1, section 2, video 2, and then the video name. You can click into this, and see the final code after this video, and go into anyone of these files. Along with this, if we go back to the root of our repo, we also have the starter files for all four of our project. The starter files are going to ensure we are on the same version numbers when progressing give you a smooth path throughout this course, and we'll take a look at how to set these up as we progress. To access these, we need to click on this green button. There will be a drop-down, and you can download this full repository as a ZIP file. Then this will give you access to all of these starters and all of the code samples too. Go ahead and download this, and place this somewhere convenient such as your desktop, and we can grab the starter files when needed.
7. Section Intro- First Look At Vue.js: To kick off your Vue learning journey, we're going to apply lots of knowledge which we'll learn during this section into a project. This project will be relatively straightforward. We will download a HTML, start a file, and then we will begin the process of converting this file over to use Vue.js. We'll learn all the fundamentals you'll need to know, such as events we'll take a look at the Vue lifecycle, finding data, we'll also look at lists, methods, and so much more. Let's jump in and I will see you in the next video.
8. The Vue Instance: In an earlier video, we mentioned Vue.js and control either all parts of a user interface. To get some experience of Vue.js, we are now going to take a simple HTML site, which is a personal site, and apply some Vue.js to create a blog section. We can jump into Vue.js, the starter project is available to download from the GitHub repository. To find this, you need to head over to this link which is github.com/chrisdixon161/vue-3-course. You can also find this as a pin repository from my profile. Once you head into this section, we're going to have all of the code which you need for this course. Currently we just have the portfolio starter, but your version will have all of the sample code which is needed for other projects, along with also the final code for each video so we can compare if you get stuck. What you'll need to do is to download all of this repository. So if you go into the code section, you can download this as a zip file. Lets open this. Then from here, we can extract all the information which we need. So lets just place this on the desktop and open this up. From here, we have the portfolio starter, which is just the basic HTML and CSS, which we're going to use to test how view works on a simple project. What you'll need to do is to rename this, I'm just going to keep this as a portfolio, and then keep this separate from the rest of the code. Open this up in Visual Studio code or your editor of choice. Then inside here, all will see is a index page, which is just some regular HTML we have of our head section, we will link to a font, we link to a provided style sheets, we have a basic header section which has a title, and then some navigation links. Then we have this blog section. This blog section just has two static HTML articles, I'm going to make this dynamic with Vue.js. The important thing now is down at the bottom, you have a script which is a link to the Vue CDN, so this is a link to the Vue Call Library which we're going to use to explore some of the Vue features. This means we don't need to do anything special such as downloading packages. All we're doing here is directly linking it to Vue.js. Then we can include some of the features in our project. To begin with Vue.js, we're going to create a new file, which is going to be a JavaScript file, and I'm going to call this the script.js. We can link this just like any regular script down at the bottom of our HTML, and we add the source attribute which is going to link to the script.js. We can also opens this index page up in the browser, we can double-click on the index or HTML file inside of our project. Or if using a Visual Studio Code, we can right-click on this tab and then copy the path, paste this in. This is the starter project, which is just HTML. We're going to begin by converting these HTML sections to be controlled by Vue.js. To do this, we'll head over to the script file. The first thing we need to do is to create a new Vue instance. We do this with Vue, capital V, and then use a function code to createApp. We have access to Vue.createApp since we're using the CDN link down at the bottom of our index page. In the upcoming videos, we'll also pass a Options object that is instance too. But for now, we need to mount this Vue application to our index page. To do this, we need to tell Vue exactly where we want to mount to. We could control the full HTML page if you wanted to. All can mount to just a certain path if you wanted. For example, this Vue instance is going to control our blog section, which is a wrapper for all of our articles. Will have this section with the idea of blog, so we're going to make use of this and tell Vue.js that this section is the part of the HTML which you want to control. This has the id blog, so all we need to do is to go over to createApp, and then call the mount method where we're going to pass in the elements which you want to link to. This is just like regular CSS selectors, so if this is an idea, we use the hash, we use the [inaudible] or we can directly target any of our elements by name, so we could target this section, and we want to target to the blog. Although here we are targeting a section element, it doesn't matter what type of elements we mount to. It could be the body section, it could be DIV, anything is completely fine. We could also create multiple Vue instances too. If we duplicated this, we could again call Vue.createApp, but this time could mount to a different section. For example, if we wanted to have this Vue instance to control the blog, and then we wanted to also control the header section, all we need to do is to pass in our different selector and let's go for the header element, which is the header section which contains our level one heading, alongside our nav links. We can also change this title to be our name, and also the same for the page title too. This means we have now created two separate Vue applications which are completely independent and can have their own data, as we'll see in the next video.
9. Data & Lists: With our app now mounted, we now need some data to work with. To do this, we're going to add some options to create app. Inside here, we're going to pass in an object, and this object is going to be where we're going to be working from now on. The first option which is going to go on this object is the data property, which sounds is used to hold our application's data, which is sometimes referred to as state. State is a function which is going to return an object. This is going to contain all of our data properties, which you can think of as a place to store the information our application needs. Just like we would do with variables. If you've used Vue 2 in the past, the data property could either be declared as a function like this or also an object, both the Vue free and must now always be declared as a function. Back over to our index, our HTML. Remember that we have a Vue app mounted to both the header section and also the blog section at just below. Which means that we can now use the Vue [inaudible] control any of the data inside of these two sections. Currently, we work in, inside of the header section, meaning we can output our data properties inside of this header section. The first one I'm going to control from the Vue application is going to be the portfolio title. So if we put our name outside here, and then we can store this inside of a property called "name". This will be a string, so add this inside of the quotations. Now, instead of having this "name" as hard-coded HTML, we can now use this data property called name. I'll put this anywhere inside of our header section. We can use the double curly braces and then access our name property. Let's go over to the browser and check this is still working, since this is linked or mounted to our header section, this name value cannot be used anywhere outside of this. If you go just below our header and try to output this name variable or to the browser, we see this is outputted as a regular string rather than being replaced with the actual name, data property. The benefits of declaring our data inside of the data section, rather than hard-coded HTML, is that the data is reactive, meaning that if any of the values inside here would change, all of the HTML sections which uses data would then be updated instantly. This is a pretty simple example using a string of text. But what if we had multiple items, such as an array? Well, we do have three values inside of our unordered list. Have the home, the portfolio, and also the contact me section. Back over to the script, we can now see how this would look as a data property. The second value separated by a comma is going to be links. I'm going to set this up as an array. This first one is going to be home. The second link was portfolio, and the third one of contact me. Since this is an array containing multiple values, we do have some options of how we can output this. The most basic option is to output this directly as a data property. We could output the name of links. This will be directly output just like a string. However, this is unlikely to be what we want. Instead, we could access each one of these links individually. Just like a regular array, we could use the double curly braces, access our links, and then access each individual link by the index number. The first value inside of an array is zero, the second one is position one, and the third one of contact me. There's going to be links too. Again, this will work perfectly fine, but it is a long way of doing things if you have lots of array items, a more efficient way of dealing with list items like this is to use a for loop. Up to our unordered list, we can use a V for loop, which is going to be link and links. Links with the S is our date's property stored in our script, and then the value of link will be updated for each loop. So the first time we loop through these three items, the link value will be set to home. The second time it loops through will be set to portfolio, and the third and final time it leaves through, this will be set to contact me. v-for is a Vue.js directive, which is a special attribute we add to our HTML. Directives will begin with a V dash prefix. And this particular one allows Vue.js to loop through a particular data property. We will also look at more directives during the course. Since we have three items inside of our links array, the content inside here will be repeated three times. So effectively we've repeated our unordered list three separate times. But this is not what we want either. So instead we can cut out the v-for loop and instead just repeat our list item three times. We can remove the second and third list item, leaving just one in place, which we're going to repeat for each item inside of links. We can then replace this with our link variable, save this, and our three links and now back in place. So just to recap, we have three items inside the links. The first time loops through, link will be set to home. So this will be replaced with home. The second time which will be replaced with portfolio and so on. Data properties can also be over data types too, such as booleans and also numbers, and also objects to which we're going to discover next.
10. Looping With Objects: We can also work with objects in Vue.js too. We're going to do this to add some some posts to our example. For this, we can use the second Vue instance which we created for our blog section into this script. We can not setup the options object for the createApp which is mounted through our blog section as an object, and we're going to work with the data property once more. This is a function just like above, which is going to return all of the data which we can use in our blog section. Inside here we're going to access our posts which is going to be an array. Each one of these posts is going to be stored inside of an object which is going to have an ID, a title, and the body section. The first ID is going to be the value of one separated by a comma, the title for the blog post. This is just [inaudible] so you can add anything you want to this blog post. The third item is the body, which is the actual content of our blog post. I'm going to grab the content from our index page. As a string, we can paste this inside of the body section. If you do see any errors just like we see here, this would be because we are using multi-line text. We could go through each one of these lines individually and bring them to be all onto the same line. Just like this, but this is not an efficient way of doing things. Or another way to do this is to use the backticks. So rather than these quotations, we could replace these with the backtick icon. This will now allow us to use multi-line text. Just to give us some content to work with, I'm going to copy this object, and then I'll paste this in for a second blog post. The ID of two, change the title. This one is going to be using the Vue CDN. Then the third one, which has the ID of three. This one is going to be how I mastered Vue, which you can write when you finish this course. Even though these posts are objects, it's still an array of objects. Meaning we can again use the V-for directive in our HTML. Down to the blog section which we are controlling with this Vue instance. We have two hard coded articles. For simplicity, let's remove the second one. We can now use a V-for loop to repeat this as many times as needed. We want to repeat the article. So we can add the V-for directive. This time, this will be post in posts. Remember that post is the name of our array and the post variable is the name for each array item. We can now access this inside of our article. Inside the double curly braces, we can output our post. Let's see how this looks in the browser. If we output the post, this is the value of our full post object. This will be repeated three times. Since we have three separate blog posts, is also unique. We have the ID of one, the ID of two and the ID of three at the bottom. Rather than accessing this full blog post, we can scripture this by accessing any of these properties. This was the post title. We can access this with post.title. Then, instead of this text inside of the p elements, we can access the post.body. This all works now perfectly fine. Back over to our script, if you were to add a lot more data properties just like this. Our Vue instance can quickly become really crowded. To help with this, we can also take our array of objects and place this outside of the Vue instance. Let's grab all of the contents of the array, including the square brackets. Grab all of this, leaving just the post variable. Outside of the Vue instance, we can create a regular JavaScript variable. This is called posts. Then, we can set this equal to our array. This post array can now be referenced down inside of our Vue instance. So the post date property can be equal to our posts array. Let's check this is working. Over to the browser. The free blog post is still in place. I'm also, using the ES6 shorthand property syntax, since the name and the value are both the same, we can shorten it to just the single value, which also works completely fine too.
11. List Indexes & Keys: At the moment, we are not making any changes to any of our lists, so Vue.js doesn't really have much work to do. However, in real world applications our data may often change, either from the user adding something into a form or an update from our server. Maybe even a new blogpost being added and updated with an API. Once Vue receives these updates since we used the data property, it will update our HTML with a new value since data properties are reactive. This is simple enough for something such as a string of text, but what if we had thousands of blogposts? Imagine we deleted or updated one right in the middle, how does Vue.js deal with this? The answer is to use a special attribute called a key. I'm going to add this over to our blogpost, which is surrounded in the article. Just after our v-for loop, we can add in a key attributes and set this equal to a particular value. This key is a way to provide a unique identifier to each one of these list items. If we had hundreds or even thousands of blogposts, having this unique key will allow Vue,js to better identify each element when the time comes to update or reorder. If we never use keys, you would need to refer back to an algorithm to detect any of these changes. This key should be unique, such as a product ID or for our blog, we also have a unique ID for each blogpost. Let's take look. We have a unique ID which is stored in this ID property. We can pass in the post.id. If we add an attribute in just like this, Vue.js would read this key value as a string called post.id. Rather than extracting the actual values of the variable, which is the value of one, two or three. If we were to insert some JavaScript or a variable, just like we use in here. We need to make this key dynamic. The way to do this is to use the v-bind directive. This is a prefix which is gone to tell Vue.js to read the value inside here as a variable rather than the string of texts which is post id. This is another Vue.js directive. This can also be shortened to simply just use the colon. This is our key now taken care off. Also when using the v-for directive, we can also access the index number. This is the position of each one of our items inside of the array. Our first blogpost is the position 0, then position 1 and so on. To do this, we can wrap our variable inside of the brackets just like this. We can still access the post, but we can also access the index number 2. If we wanted to, we could output the index. Again inside of the double curly braces, we have position 0, position 1, and then position 2. This is also just like JavaScript, we can add plus one. This could be the first value, the second value and so on. This can be useful for things such as numbering our items we display. Well, for our blog, exercise is not required. We can also remove this too. One of the key things to note is, it is recommended to not use this index position as a value for our key. This is because if no key is supplied, we will make use of the index position anyway. Also alongside binding this key attributes, we can also bind other attributes too, which we are going to take a look at next.
12. Binding Attributes: We had a first look at binding attributes in the last video using the key. We're now going to move onto looking at binding class and style attributes Vue. To do this, we can create a darkMode for our blog which the user can toggle on or off. To create the styles for this darkMode we need to go over to our style sheet, and then down at the bottom we can add a darkMode class which we're going to toggle on or off. This is going to be pretty simple. We can create the background and set this to a darker color, the one I'm going to use is going to be 38383a. To offset this we'll change the color to be a lighter value of whitesmoke. To apply this darkMode we are going to add this as a class, and surrounding all of our content, such as the header section and our blog we have this surrounding div element. In the opening tag, just inside the body, we can add this as a regular class and this will apply our changes. This works fine but we need a way to toggle this on and off, and also to integrate this into our Vue app. Currently we have a small problem which is we're using Vue to control the header section and also the blog section independently. But when we toggle only darkMode we want this class to be applied to the full site. We can simplify things by merging our two Vue instances. To do this all we need to do is to grab our posts from the second app, and then paste this into the data property for our first section. We don't have any more data inside here, so we can remove this full instance and then we're going to mount this to the body section. This means that the Vue instance is now controlling all of our webpage. The first step to allow this class to be toggled on and off is to set this as a data property. A data property is going to be a Boolean, and I want to call this the darkModeSet, which will be a initial Boolean value of false. To dynamically toggle the darkMode class based on this particular data property, we need to pass our class attribute as an object rather than a regular attributes, just like this. Inside of here, we need to add an object using the curly braces. We only want this dark class to apply if the data property which is darkModeSet is going to be equal to true. Currently this is false, so Vue.js won't apply this dark class. We can see this if we refresh the browser, the dark class no longer applies. But if it were to toggle this to be true, Vue.js should then apply our class. But if we refresh this we don't see any changes, and this is because just like when we set the key property earlier on, we need to bind this as a dynamic value. Currently, Vue.js is reading this as a string, which we can see if we go into the developer tools. I'll right-click and inspect the "Body section" and then immediately inside we see that our class is output as a string. This is not a valid class, so instead what we're going to do is to use the v bind directive. Or just like earlier we can shorten this to be a colon. Now instead of a string this will be interpreted as JavaScript, and if we refresh the developer tools we'll see the class of dark has now been applied, and this has also been applied to our project too. Alongside this class we can also add data properties to inline styles too. Let's remove this. I'm going to setup a regular style property which is just regular HTML. Inside here we can set the color value, let's go for a value of hotpink. This is just regular HTML, so if we refresh this will then be applied to our projects. We can also move this color property to be a data property too. Inside of the script let's set the textColor to be the string value of hotpink. Then as we did with the class attributes, we need to pass this style in as an object. The first step is to surround this in the curly braces and then replace this string of hotpink with our data property which was textColor. This is a variable, so we need to bind this using v bind or the shorthand which was the colon. Refresh, and this time our styling has been applied from the data property. If we did have multiple style properties which we wanted to add like this, our HTML could quickly get messy. Instead we could remove this, leaving in the style attributes, and then over in our script we can create a style object as a data property. We can remove this, and then instead we're going to setup our darkMode.CSS using the same styles that we applied down at the bottom. Let's comment this out and then setup our background which was the hex value of 38383a. Also the color for the text, which is going to be whitesmoke. This darkMode object can now be directly placed as an attribute. Since its value is already an object, we don't need to place in it the curly braces. This can just get directly added in. Let's give this a try. Our darkMode has now been applied. We can even add multiple style objects too, for example if we wanted to setup a base font family or some base styles, we can setup a new object, and let's set the fontFamily to be the value of monospace. If you are not familiar adding CSS properties with JavaScripts, all of these CSS properties need to become a case. If we were using CSS directly in our style sheet we would set the fontFamily all in lowercase and separated with the dashes. Instead in JavaScript, we set this as camelCase so each word after the very first one begins with a capital letter. Now we'll have these multiple style objects which we want to add. These can be now placed in as an array. We can pass in the square brackets, and then our second object, which was base, save this, and the styling for both of our objects have been merged. If we go into the developer tools, we can see this by hovering over our main div. This has our background color, it has the textColor, and then our fontFamily from the second JavaScript object.
13. Mini Challenge- Dynamic Links: Using what we've learned so far, have a mini challenge to try things out yourself. What I would like you to do is to go over to the header section which contains our free navigation links. Currently, each one of these links has an href attribute which is pointing to the forward slash. Rather than having this hard-coded, we could create this as a dynamic attribute, and set the href value inside of our data property. If we go to our scripts, a good starting point would be to replace this links array with a array of objects, just like above where we created our blog posts as an array of objects, and it can use this array as a good starting point of your links. This is number 1 covered. Then next, the object should have URL property with a link. Instead of the ID, the title, and the body which we have in our blog post, we can maybe changes to be the ID, the name, and the URL. The URL can be made up, it can be an external link, or an internal link to a fictional page such as forward slash portfolio. But now, don't worry if the page which we link into is being created or not. All we are worried about is creating a dynamic link. Then just as with the blog posts, number 3 is to loop through this array using v-for and set a unique key. Finally, using the <a> element, we're going to create a link to each page. As a hint, we can use the v-bind directive which we learned about in the last video for the href attribute 2.
14. Dynamic Links: My Solution: I hope that went well for you, and I'm now going to complete this challenge and show you my solution. Starting with number 1, which was to create an object containing all of our links, just like we did with the blog post. Outside of Vue instance, our constant for our links which is going to be an array of objects. The first one is the ID of 1, the name which is going to be our home. The third property is going to be for our URL, this is going to be the location where we're going to link to. The home link is going to be the index.html separated by a comma. We need two more links, the ID of 2 and this one is going to link to our portfolio, and the URL is going to be portfolio.html. As mentioned in the last video, it doesn't matter if this page has been created or not, all we are we interested in at this stage is to create a dynamic link. The third one is for contact me in the contact.html. Remove the array from our data property and we can either set this via links or we can you see the ES6 shorthand. Since both of these words are the same, this takes care of step one and step 2, and for step 3, we need to loop for these links and I'll put them in our HTML. Over in our Index page we already have the for loop setup, so this is taken care of. The next step is to add eight dynamic key, which is going to be link.id. Since link is now an object, we also need to pass in the name and it should all now still work in the browser. Finally, step 4 is to use the <a> element to create a dynamic href. First of all the v-bind directive, or we can once again use the shorthand which is e colon, and then rather than the forward slash, we can access the link.url. I can try out all links in the browser, home link will still link to this current page, the portfolio. This links to forward slash portfolio.html and the forward slash contact me is working too. Again, don't worry if this is linking to a file which we haven't created, all that matters is our links are now working.
15. Javascript Expressions: In our HTML files, we have already used the double calibraces so far, also referred to as interpolation to outputs our data properties, and also inside our loops to output a variable too. If required, these double calibraces can also support JavaScript expressions too. A small symbol could use these to perform a JavaScript calculation and then output the result. Anywhere inside of here, let's go just below the header. We can use these double calibraces and create a simple addition such as 1 plus 3. If you just will evaluate this expression and then output the result of 4, we could use our JavaScript expressions too, such as divide or we could say 10 divided by 2, the value of 5. We can also use built-in JavaScript methods too. For example, we could access the math object and then select a method called random. This will then generate a random number between zero and one, each time we reload the browser. We can also access our existing data properties, such as the name and transform this using JavaScript methods such as toUpperCase. It will then take our original name, which is placed in a date property, and then transform this to be uppercase before outputing this in the browser. We could go even further to an odd things such as the JavaScript ternary operator. We could check if the name.length is greater than zero. This is currently true since we have more than zero characters inside the string. If this is true, which we know it is, so we're just going to outputs a string of welcome, followed by our user's name. If not, we use the colon to provide a second condition, which is going to be "Welcome Guest." This is regular JavaScript and it's nothing specific to view JS. The only view involvement is we're outputting this using the double calibraces. We know our name.length is greater than zero, so we see the message of welcome on our name. If we were to create an empty string. For example, if a user is not logged in, we would see the message of "Welcome guest". Another thing we can do to, is to add expressions into our dynamic attributes too, just above where we set this dynamic style property. Let's say we wanted to only set the darkMode object if a certain condition is true. As a random example, we could say if 10 is less than five, if this is true, we're then going to display the darkMode, or if not, we're going to output an empty string, meaning no class will be applied. Save this and refresh. Ten is less than five, so the darkMode will not be applied if we change this to be true, so 10 is greater than five, darkMode should now be applied to our project, or as a more realistic example, back over in our scripts. We have this darkMode set property. We can now use this to conditionally render our darkMode rather than this number value, we can say "darkModeSet", which is currently true, so darkMode will stay enabled. If we set this to be false, it should now be removed. We can also remove our welcome message too. Toggling this darkMode on and off manually is not ideal. In the next video, we're going to set this to react to a button.
16. Event Handling: In addition to the directives which we have looked at so far, which include V4 and V bind. We're now going to look at another useful directive called V-on, which will allow us to react to certain events. This V-on directive is also passed to our elements, inside of the opening tag as an attribute, and we're going to use it to change between the light and the dark mode. When the user clicks on a button, bringing us back in our index.html. Down inside of our blog section, we're going to add a HTML button. We can then include inside of the open side of the V-on the directive, which is going to listen for a click events. We can also listen for over events too, just like in regular JavaScript, such as on-submit, on-mouseover, on-cube, and so on. The only difference is we remove the on-prefix, meaning not in events such as on-mouseover would simply change to mouseover. We however, on this occasion, want to listen for a click on this button, and then inside of the quotations, we want to actually do something when this button has been clicked. In this case we want to toggle between the dark mode, and we have this set as a data property called dark mode set. Inside here we're going to toggle list by setting dark mode set to be equal to the opposite. The exclamation mark means that if dark mode set is currently true, this will toggle it to be false, and also the opposite applies too. Let's try this, in fact, we need to add some text in between our button too. Click on this, and this will now toggle our dark mode. We can see the output of dark mode set, if we grab this, and then we can output this in between the curly braces. To see this more clear, this is currently set to false. Like this, it'll be true, false, and so on. As well as this V-on syntax, it is also a shorthand too, just like when we shortened the bind to use a colon, we can shorten V-on to use the at symbol, and this will work exactly the same. This toggle button is not the best looking, so instead, we're going to replace this with a HTML entity. We can then toggle between a son and a moon icon, depending on our toggle state. The two icons which we're going to display are going to replace this toggle text. First of all, let's output our two icons. The first one is the ampersand, the harsh, and then 9788 followed by the semicolon. The second one is the ampersand, the harsh, and this is 9789. This will give us our two icons which we need, but we only want to show one of these at a time. We can use the JavaScript ternary operator to switch between these two icons. Open up the double curly braces and we can check if dark mode set is currently true. If it is, we're going to output as a string, the first icon, which is the sun. If not, we use the colon and then pass in the moon. Reload. We're currently in the light mode, so we see the moon. Click on this and this will switch to the sun. As we mentioned before alongside listening for our click events, we can also listen for over events too, such as mouseover, and this should now toggle our state each time we hover over this particular button. However, for this example, a click event makes more sense. I didn't award JavaScript expressions directly into the click event like this, is okay for small examples, but is generally considered better to extract this out of our HTML and places into a function or a method, and this is what we're going to discover next.
17. Methods: Inside of Vue.createApp, so far we've only been working inside of this data section. Now, we are going to add a new section which is going to be for our methods. This will allow us to do lots of cool things such as changing our data, and generally making our apps a lot more interactive and useful. If you are familiar with functions in JavaScript, methods do the same thing. For instance, they have the name method because they're part of this object. Whereas our data section was set up as a function, our method to setup as an object because we're going to call them manually. Make sure we are outside of our data section. Let's scroll down to the closing curly brace. Then after this we're going to set up our methods, use it as an object. The first method which we're going to use is going to be to toggle our dark mode. This is because we don't want too much JavaScript logic replaced in our HTML file. We're going to abstract the toggling of our color scheme into this method. Let's cut this out and paste this inside of our method. Then we need to change dark mode set to be this.darkModeSet. The reason we're using this, this keyword is because both the data section and also the method section is part of what is called the options API. The sections amongst others which we'll look at soon, have access to each Vue properties by using this keyword. For example, our methods can access anything from the data section as long as we use this keyword, so could access this.name, this.links and also in return the data section can call our method, or we could say this.togglemode. Now all that's left to do is to call this method from our index page. Passing it toggle mode, refresh the browser, and now we can still switch between our two color versions for this time by extracting all code into a method.
18. Raw HTML: If we wanted to, we can also output HTML using Vue.js. Back over inside of our script.js and up to the very top, we have this array of blog posts. Let's say we wanted to format the body of this blog post by adding some HTML elements inside of this text. For example, we could cut out any of these words and then create the strong elements. Placing this word back in between. We could also add any of the elements too such as the break tag, which will then place this onto a new line. Let's go back over to the browser and see what effect this will have. We can see we're not quite getting the result we want, the strong elements and also the break tag has just been output as part of a string. This is because we're outputting this over in our index.html in between these double curly braces. These double curly braces will output the value as plain text. This makes this curly braces not good choice for text containing any HTML elements. Instead, Vue provides a new directive called V-html. As always, we add our directives as an attribute, and this will replace our double curly braces. This is V-html and then we can set this equal to our post, the body. We can also remove this, refresh the browser. Our strong tag has now taken effect and also our break tag too. This all works completely fine now but for security reasons, we do need to be careful when using V-html. We should only inject HTML from trusted sources. For example, we should never use HTML which is being provided by the end user of our site as this could lead to potential cross-site scripting attacks. It's really important when using this directive that we are completely sure that this source is safe. This is how we can use V-html. Next, we're going to discover life cycle hooks.
19. Vue Lifecycle: When we create our Vue upper overview instance, there is a series of stages going on behind the scenes. The Vue documentation has a pretty good diagram of exactly what goes on when we create a new Vue app. You don't need to remember these or fully understand for now, but do be aware that we can run some code at each stage of this life cycle. For example, at the top we see Vue.createApp while passing our options. This is the initial stage which we've already looked at, and then we want this to the DOM with app.mount. After this instance has been created, we have access to the beforeCreate and also the created hooks. These life cycle hooks can be added to our object, which you pass into createApp alongside the data and the methods which we are currently using. Let's take a look at these first two life cycle hooks over in our scripts. Alongside our data, we are first going to look at before creates. The second one separated by a comma is the created hook. Inside of these two hooks we can add any code which you want to run at these first two stages. To be able to see exactly when each one of these hooks will run, we can do a console log. The first one I'm going to add some text of beforeCreate, so we know exactly which console log is running, and then remember using this keyword, we can access any other properties from our Vue instance, which has our data's actions. We can output the value of our name using this.name. With this we can also output this inside of the created hook over to the console. Right-click and inspect, and also mainly to refresh too. We see that the before create hook returns a value of undefined. But below this inside of the creative hook, we have access to our data property. Why is this? Well, it's not until the created stage where Vue has finished processing and setting up things on our options object. Which has our data and methods. Which results in anything from our Vue instance such as our data properties to be returned as undefined inside of the before creates hook. This means we would only use this for something such as an API call, which is not linked to any over sections, such as our data properties. We can also see this if we go back to the drawing. We can see inside of this section we don't have access to any of the viewer activity until after the beforeCreate hook. After this we then go down to the template option, which gives us two separate options. We will take a look at this template option soon when we look at components. This is basically the section where we output all of our HTML, which includes the dynamic sections with a double curly braces. If this template option isn't available, instead, it will go down to our section which we mount to, and evaluate the contents inside of here. After this, we have a series of stages when the application has been mounted to the DOM, we can access before mount or as soon as the app has been mounted. Once the app has been mounted to the DOM, we can also listen out for any changes too. We can then run some code before the update has happened or after the update has finalized. The final two stages is when a component is being removed from the DOM and we can access it before a mount hook and also the unmounted hook too. These final two stages are useful for any cleanup work, maybe want to stop listening for any events, and this is the place to do it. See how all this works in practice and now we are going to make a simple API call to get some blog posts from an external source. The source for this blog post is going to be available from JSON placeholder. This is a very simple and reviews API which we can use to get some sample data such as our blog posts. We can see that we can get blogposts, we can get comments. We've got albums, photos, todos, and also users. We're going to access the /post section, which is going to return a 100 sample blog posts. So this is pretty simple, we just add /posts onto the end of this URL. This will return a list of 100 blog posts in JSON format, and we can also make a call to this URL from a Vue method. Go back over to Visual Studio Code and into our script, we can call this from inside of our method section, and the method I'm going to call it is going to be getposts. Inside here, we're going to make use of the Fetch API to make a call to this URL. Paste this in as a string, and this fetch API is just regular JavaScript. It's nothing to do with Vue JS. We can then store the data which we got back inside of a variable called response. Since we are reaching out to an external API, this is going to be an asynchronous call. This means that the data which we get back may come back in a few milliseconds. It may take five seconds, it may not come back at all. So to deal with this, we can make use of a single weight, which is a JavaScript feature. Which will allow us to wait on the data to comeback before we actually do anything with it. To do this, we can mark our method as asynchronous. So we add the async keyword just before, and then we're going to wait on our data coming back. This means if we try to access the response variable before this data comes back, it won't throw an error. Now this leaves us with the response object from our API call, and then we need to extract the JSON from the response. To do this, we're going to access our response and then call the JSON method. This is going to extract the JSON from our response, which we can store in a constant or a variable called data. We'll also [inaudible] this to make sure that this is finished before we access our data, and then would log this to the console. Now before we go any further, let's take a step back and think about what we're want to do. We have a list of blogposts which we're getting back from the API, and this is stored inside of this data variable. Leaving us with two things which we need to do. First, we need to pass this data up to our data section and add this to our posts. The second thing we need to do is to actually call this method. As we looked at earlier, we could add a button to our sites and make this call, or this would not be a good use. Instead, we want to do this automatically, and we could do this from a life cycle hook. We can use the beforeCreates hook. Since remember this is called before the app is created, so the get post method will be undefined. We could use mounted once the app is mounted to the DOM, or we could go even earlier and use the created hook, which we looked up before. We no longer need this beforeCreate, so we're going to move this and then go down to the created section. Instead of doing a console log, we're going to call our getPost method, which we can access with this.getPosts. Over to the browser, and we can open up the developer tools. I'm going to say reponse is not defined, so we've got a spelling mistake. There we go. Response. Refresh. There's our console log with 100 different blog posts. Each one of these blog posts has an ID, has a body, a title, and also the user ID which created it. This is exactly the same data we already have, plus this extra user ID, which we can ignore if we don't want to use. We are now going to use this data to replace the free blog posts which we already have. First, we're going to unset the blog posts we already have from our Data property. To do this, we can scroll up to our posts and then set this to be an empty array, and we can now update these posts from our method. Do this rather than a console log. What we're going to do is access this.posts, and set this equal to our data. Over to the browser. This now leaves us with a 100 different blog posts inside of our page. The reason this works is because the data which we got back from the API, which we'd have seen in the console log, is structured the same way as our original blog posts. We can see this inside of our index page. Down in the blog section, we have access to the title, the body, and these also have an ID too. If the data which came back from our API was slightly different, for example, if the body section was titled content, all we would need to do is change this inside here, and we will be good to go.
20. Section Intro- Component Basics: When working with front-end frameworks or libraries like Vue.js, one of the most important concepts to learn is components. In the upcoming videos, we're going to discover how Vue.js uses components, what they are, the different component types, and then we can look at things such as how we can pass data between these components. Let's start in the next video, and we'll take a look at what exactly a component is.
21. What Are Components?: Vue.js and some other friends and frameworks or libraries have this concept of building our applications using components. The reason behind this is to break up our app into smaller pieces, making our project more easy to maintain, organize, and debug. But what is a component? Well, consider this image which is from our upcoming project. This is a countdown app where we will add events and they will be displayed in date order. We can also see the number of days to the event too. We could build this app completely in the App.vue file. But even though this is not a huge application, it may quickly become crowded and unorganized or being in one single file. To help with this, we can split up our app into parts or sections, like our button at the top, which can even be part of the App.vue file or we could move them to be a separate component. Also with our events too, the code for these could be taken out of this App.vue file and placed into an independent components. Isolating this code into a component like this means we now know where to look to edit this code rather than scrolling through a large file. Also, if there is a problem with this events section, our code is now isolated too, which will help with fixing the issue. This project also includes a form which we'll use to either add a new event or to edit an existing one too. This would also be a good use case for a component. There is no hard or fast rule to determine exactly which path we should make into a component, it's something which you have to judge yourself when a file becomes large or heard to maintain. This may be a good sign that you need to outsource into a separate component, or you can also organize by features too just like we see here with this edit form. The number of components we create does not hinder our performance or increase the size bubble up, since we use web part to bundle them altogether efficiently in the end. It's in this bundle which the browser uses for our single page applications.
22. First Look At Components: The previous slides used the concept of single file components, it's a component who plays into a dedicated file to keep things more organized. To do this, we need a much more sophisticated setup which we'll look at in the next section. These components placed into its own file is probably the most commonly used type of components. But first, we're going to look at some different ways of writing this beginning with the global component. The first thing I'm going to do inside of our script.js is to store our view instance inside of a constant called app. Now, we have this application stored into this app reference. We're then going to use this call mount, so app.mount. We'll then remount this to the body section. If we refresh the browser, we don't see any changes inside here, but now, having this separate reference to our application means we can now call a method called component. This app.component takes in two arguments. The first one is the name we want to give to this component, and mine is going to be app-header. When using the CDN link just like we are, the name of this component should be all lowercase, more than one word, and separated by dashes. This is because we will insert this back into our HTML just like a regular element so we don't want any naming clashes. This naming convention also means that we don't clash with any regular HTML elements such as the header, the nav, also a regular hv for example. After this, the second argument separated by a comma is going to be an object. This object is like a mini application. It can have its own data, its own methods, and basically, anything which we have just above in our regular view instance. Alongside this, it also has a template which is a string, and this is going to be the HTML which we want to output inside of the browser. This can be any HTML code such as a level one heading. Let's just add some text inside it here for now. Now, we can use this app-header inside of our index page just like a regular HTML element. Let's comment out the header section and then we can replace it with our app-header elements, save, and this is now output in place of our regular header. Now we know this is working, we can now grab our regular header section. We can uncomment this out and cut this out place, and then we can now place this as our template string. [inaudible] from all of these red underlines, but the text editor is highlighting a problem. This is because we are using quotations to surround all of this HTML. This work perfectly fine when we use a single line of HTML, just like our level 1 heading. However when we have multiple lines of HTML just like this, we need to replace these quotations with the bacticks. When we use the view CLI to setup our project soon, this will give us a cleaner way of writing our HTML code inside of these components. But for now mover to the browser. This now shows up inside of a header section but we are missing the variables. We no longer see things such as our dynamic sections, which is the name, and also the link just below. For this to work, we can also set up the data section which it needs for this template. So scroll up to the data section which we've been using. We need the name and also the link so I'm going to cut these out of the view instance and then place these back into our component. We set up the data just like we did above. This is a function which is going to return an object and we can paste in our name and our links. Don't forget to separate the data section and the template with a comma. This now places our data back into the browser but this time, from a component.
23. Props & Re-Using Components: Along with isolating our code, another benefit of using components is they can also be reused. Not only that but we can also reuse the same components and also pass it different values. To see this, we are going to move over one of our blog posts into a component. We can cut or replace the full article section of our blog over in our index.html. Just like in the last video, we're going to create a new component. Again, make sure this is just above up the amount. So again, this is up.components and the first argument is going to be the name, which I'm going to call the blog-post, separated by a comma we pass in our object. This is going to take in our template, which is multi-line. So again, we use the back ticks paste in our article. For our simplicity we're going to remove the for loop. We can also change this to be simply a title and the body section. So we're going to pass this down a single post. Then just like we did with the header component, we're going to output our new blog post inside of the HTML. This is going to be placed back inside of our blog section. This time though, rather than having the data inside of the component, just like we did with the header section. This time we're going to pass the data down to this component using props. Props are just like regular attributes which we can use to send the data the component needs, and this component needs the title and also the body and the way to do this, just like an attribute, is to go into the opening tag of blog posts. First of all, we can pass the title and this is just some sample data for now so we can give this any title which you want. This also needs the body section too, so we can give this a second attribute or a second prop. Again, this is going to be a simple string. Since this is a compound, we can also reuse these two. So let's copy and paste this in. Change the title. Each time we do this and reuse the components, each component incidence is independence of the other ones and also has its own data, which is also referred to as state. Meaning although we have two separate blog posts, they will not conflict with each other. So we've passed our two props, we've passed the title and also the body. However, though these are not automatically assigned to these variables. One thing we need to do first is to declare exactly what props this component expects to receive. Since we are receiving multiple props, we can add this as an array and then add a comma at the end. The first prop is the title, and then the second one is the body. This now means both of these props are now available to use inside of our template. Let's try this out over in the browser. When I hover two blog post components, each one with independent data. Although this works completely fine, it's not an efficient way of creating, say, 100 blog posts. So a better way to do this would be to reuse this component with a V for loop. Back to our index page and rather than having in two separate blog posts, what we're going to do is just have our single blog post. Then we can use a V for loop. Inside of this V for loop, we want to take advantage of the data which we already have. This is all of the blog posts which we have stored inside here. So we can say post, in posts pass in a key, which is going to be our post.id. Now rather than passing down our title and our body as plain text, instead we're going to pass down a prop or post, which is going to be equal to our post from the loop just above. Since this is a variable, we also need to bind this using a v bind syntax. This is passed as the contents of our variable rather than the string of post. Now this blog post is being past a object called post over to our script on down to our components. Instead of receiving both the title and the body, this is now receiving the post as an object. Then we can access the post.title and also the post.body. Let's check this out. Our blog post component is now reinstated. But this time they have all the data they need which has been passed down as a prop.
24. Local Components: The type of components used so far using this app.component is classed as a global component, meaning they are instantly available to use anywhere in our application. This also includes subcomponents too. By subcomponents, I mean components which are nested inside of others. For example, not that it makes a lot sense, but we could also place our header components inside of this blog post. This template is just like our HTML, so we could place this in just like we did in the index page, but this was app-header, and also close this off. If we now save this and refresh our project, we see that our header title and our navigation links is available in each one of our blog posts, and this is because they are globally available. So unless you specifically want this behavior, we can also restrict the use of components by instead registering them locally. Local registration involves moving away from app.component and creating regular JavaScript objects. These objects need to be created above Vue.createApp, and we'll see why in just a moment. The first one is going to be for our appHeader. Just like we said, this is going to be a regular JavaScript object. The second component was for blogPost. Now, we can grab the contents of both of our components and place them inside of these objects. So the first one is the appHeader, so I'll scroll down to our appHeader component, and then we can copy all of the contents inside of these curly brace. Don't copy this curly brace, we're just going to copy the contents right down to the closing header section. Let's grab this and then paste this inside of our object. The next one is our blogPost, and we can do exactly the same. You're going to grab the props and also the template. Add this inside here. We're then going to register our intent. They use these components inside of Vue.createApp. We can do this by creating a component section, and then add a comma at the end. So the name of the first component is going to be blog-post, which is going to be equal to our object which you created above, which is blogPost. Separated by comma, the second one is going to be the appHeader. Our components are now registered to use inside of our index page. A few things to first note, the JavaScript object, which we create above, must be declared above this createApp so we can then access them inside of our components. If we also have multiple Vue instances, we can now control exactly which components we want to appear or to be available in each one. We no longer need these global components down at the bottom, so I'm going to comment out the appHeader and also the blogPost over to the browser. Refresh the page, add our blogPost, and now appearing alongside our header. But one thing to note that inside of each one of our blogPost would no longer see this appHeader. Remember from before, if we scroll up to our components, we still have in place this appHeader components inside of each blogPost, but we no longer see it inside the browser. Why is this? Well, this app-header is now a subcomponents of our blogPost, and now that our components are local rather than global, we now don't have access to our components anywhere we want to. If we did want to use this appHeader or any other components inside of here, we would also need to pass in the components option too. Just like we did inside a Vue.createApp, we can also pass in our components. This, again, is an object, and then we're going to declare that we're using this app-header, which is going to point to the appHeader object. Save and refresh, and its local registration now place our appHeader into each one of these blogPosts. Of course we don't need this for our project, so I'm going to remove this components and also the line of code inside of here. But this is an example if you did want to locally register a component inside of another one. Now, we understand more about Vue and components. Next, let's discover a better setup using a Vue CLI along with beginning our next project.
25. Section Intro- Build Tools & Single Page Applications: Importing Vue.js as a CDN link is really useful for simple projects or for getting started, learning all about Vue. But for bigger or more real-world applications, we generally want to use a more sophisticated setup. This will allow us to break down our applications into reusable components, place them into separate files, and create things like single-page applications. To do this, the upcoming videos are going to focus on a tool called the Vue CLI.
26. Single Page Applications: In the previous project, we looked at how we can use Vue.js to work with just a single path for application. We can take an existing website or application, create a new Vue instance, and then mount it to any of the HTML elements which you want to use it on. Also as mentioned earlier, we can also use Vue.js to control our full front-end. We can use it to build what's called the single page application. This is an app which is controlled entirely by JavaScript and works inside of the browser. With the traditional way of building websites, one may click on a page link, such as this About link. The page is then requested from a web server, which then responds with the page contents, and this then displayed in the browser. Single page applications work differently to this. Instead of requesting pages on assets as we need them, Vue will build our app into JavaScript bundles. When a user first visits a single page application, the bundle is then downloaded from the server. This JavaScript bundle includes the contents of our project, such as our pages, meaning all of the navigation between these pages is handled by JavaScript inside the browser without needing to request any further pages from the server, hence the name single page application. Like any fineness approach, it has pros and cons. One of the pros is the speed is really fast. Switching between pages with JavaScript is far faster than requesting a page from a web server. This provides a great user experience since there is no loading and navigating our app is almost instant. Also since this bundle is stored inside the browser, it can also work offline too. It does also have some drawbacks though, such as downloading a big bundle can be initially slower when the user first visits your site. This can be optimized though with code splitting and we'll look at this later in the course. Also, it is generally not considered as SEO friendly as traditional server generated pages. Since the HTML content returned is minimal and the rest of the content is generated inside the browser resulting in content which is harder to crawl by search engines, although the bigger search engine companies seem to have really improved this recently. Also, as a side note, since JavaScript can run inside the browser and also the server too. It is also possible to use Vue to create what is called a server-side rendered app. This converts our JavaScript application to be more like the traditional way of doing things, where we request pages when needed. This is more of an advanced topic though and one which is not covered in this course. There is also a framework tool such as Node.js which will help with this. Let's next, take a look at some tooling which will help us create these types of single page applications.
27. The Vue CLI: In the project we've been working with so far, we've used a CDN link to access the Vue JS features. The setup is fine for smaller projects or when just working with a part of our website. For larger projects though, we often need a better setup with more features and tools. To help with this, there is the Vue CLI, which is available at cli.vuejs.org, which has plugins for many popular tools, which has TypeScript, Babel, Testing, and also to easily add packages which we may need such as the Vue router. Even if you don't plan on using many of the features or plugins, you will be likely to want a setup like this that allow us to use components more easily and also build for production. It also includes tooling such as Webpack so we can bundle together all of our files and assets to create these single page applications. The CLI is created and maintained by the Vue JS core team. Although we know it's not required since we've already built a Vue JS project without it, it is generally accepted as the standard way to create Vue JS projects. If we scroll down, there is a Getting Started section at the bottom to set us up inside the terminal. One thing to note here is that Node.js is required before we go ahead and do this. If you don't have Node.js already installed, if you'd head over to nodejs.org, and then click on the current version to download for your operating system. Also, if it has been a while since you have updated, it may also be worth going through the installation process. It is just a case of a few clicks. Node.js is a way for us to run JavaScript on the server. We won't be writing any Node.js code or in fact any server related code. This is just a requirement behind the scenes for the Vue CLI. Once this has downloaded, we just need to unzip the package and then it continue through the installation process for your particular operating system. All done. We can now close down this window. Installing Node like this will also install npm, which is the Node's package manager, which we'll use to install both the Vue CLI and also any additional packages we need for our projects. To go ahead with the installation, you will need to head over to the terminal. You can use any terminal for this such as the built-in Mac Terminal, you can use Windows PowerShell, and there is also one built into Visual Studio Code. If you open up, select the terminal option, and then go to "New Terminal", which will open this up at the bottom of our text editor. Over, I'm going to be using iTerm for this project. The first thing to do is to check the both npm and Node are both correctly installed. First we can type in node and then dash v. This will return to us the current version of nodes which is installed. Next, npm -v, which then returns the installed version of npm. Both of these commands should return the version number, but if you do see an error, you will need to correctly install Node and npm before continuing any further. Back over to the Vue CLI homepage, we have this installation section down at the bottom. We can either use npm or the Yarn package manager. Either way is completely fine. If you don't currently have Yarn setup on your machine, I would recommend using the npm command since this doesn't need any additional installation. Let's copy and paste this is over, hit "Enter", and this will go through the installation process for the CLI. If you are a Mac user and hitting any installation errors relating to permissions, you may also need to add the pseudo prefix before this npm install, then you'll need to enter the password when prompted. Or even better, you could also fix the permissions which are blocking you from installing. Once you've done this, we can then use the Vue create command, followed by the name of our project. But first, we're going to use the cd command to change into our directory where we want to install our project. I'm going to change into the desktop, hit "Enter". Well, you could place this anywhere which you want. We can then use Vue create and the name of the project which I'm going to call the CLI demo, hit "Enter". At the time of recording this, the Vue CLI gives us the option of either Vue version 2 or Vue 3, which also comes with the Babel and also ESLint setup for us. Select "Vue 3". Also, remember that in the future, the option for Vue 2 may not be there. Sometimes the options do change, but it is usually pretty self-explanatory. It should now leave us with our demo project on the desktop. Now we need to change into this demo directory. Like it says, Babel can use cd to change directory into our CLI demo. Now we are inside of this demo, we can then run our development server. If you use an npm, we can type in npm serve, hit "Enter". In fact this is npm run serve, hit "Enter". This will now set up our development server. Once this is done, it will give us a link which you can now open inside the browser. See the starter project all setup. All we need to now do to edit this is to open up this project inside of our text editor so we can plug this over. Now we're good to go write in our Vue JS code. This project, however, looks a little different to the previous project structure. Next we'll take an overview of exactly what's included with this project setup.
28. Project Structure: I just want to take a few moments to look at the project structure, which we've just created. If we look in the sidebar we'll see all of the files and folders which are now inside of our project. The first folder we see is the node modules. This folder is where all of the MPM modules are installed. If you have used node before, you will be familiar with this kind of setup. Typically, projects which use node is split up into packages. Each one add in some functionality. We may have a package, for example, which formats dates, one which does form validation. We can also add our own packages in here too, using MPM which we have just set up. This is where things such as the vue.js code libraries located, rather than linking it to the CDN script like we did in the first project. If you are familiar with all of this, an MPM install has been run for us when we set up the project with the CLI. After this, we have the public folder which contains our static files, such as any images we want to use, and also any HTML. First we have the favicon, which is the icon which will appear in the browser tab. Then we have the index.html. If we open up this index.html, we don't see a lot of content inside of here, and as I mentioned in the single-page application video, these types of apps have a minimal html structure. Then the rest of the code will be bundled and injected into this div with the ID of App. Just like in the first project where we mounted the view instance to a particular element in HTML. This is the location in this project, which it will be mounted to. We'll see how this is done very soon. After this, we have the source folder. This folder is where we do most of our work. This is where we will be at in most of our code for this project. Here we have the assets folder, this is where we store the assets for the project such as any CSS files any icons our phones and we can also place images inside of here too. Below this, the components folder is something which we'll look at in more detail soon. But rather than creating components like we looked at in the last section, we can organize them into separate files. And this is where we're going to be storing them. The single component we have setup is this HelloWorld example. This is an example of a single file component. This is generally split up into three sections. First of all, we have the template section, which is going to contain our HTML code. This is similar to the last section where we created our global and our local components, we had the template section as a string. However, with a single file component, this is much more convenient since we can lay this out just like regular HTML. We can also use the double curly braces to insert our data. So this should all look pretty familiar. If we scroll down to the end of the template section, we then have a script section. It's actually is where we're going to add both our JavaScript and also our view JS code. Inside of this export default section is where we can setup things such as our data and methods. Then finally at the bottom, we also have a style section where we could place our CSS, splitting up our code into single file components like this. It's made possible by using bill tools such as web Park, which bundles everything together, throws into one output file which is then used for production. Next we see the App.vue file, and this is a special Vue file which you can think of as the main file or the main component. Just like with the HelloWorld example we just looked at, this file also has a template section, a script, and also the style section too. We could write our full project code inside of this one single file. However, it will get very big. Instead we generally take our code and break these up into smaller components. Since these components in their own separate file, we do need to import them, just like we see here, before we can use them in our template. So with all of this Vue.js code in place, we now just need a way to mount this to the dom. We already seen in the index.html, we have this mountain point, which is the div with the ID of App. This mounting is done in the next file, which is the main.js. This is the main JavaScript file for our project and the place where we can also add plugins to such as the router. Since we are now using the view MPM module, we'll now need to import the parts of this library which we need. The main part which we need is this createApp and we're using the curly braces. So we import only the single section of the vue library. This is also referred to as a named import. Now we have access to createApp. We can use it just like we did in the earlier project and then mount this to the div with the ID of App, which we've just seen in our index page. In the earlier project, we added an object to createApp. We did this manually by passing in an object and then we save things such as our data. But now instead with this type of setup, we import the root components, which is the App.vue. Then all of our components are going to be nested inside of here. This may all seem a little complex at first, but all we are generally doing is creating an index page or an HTML file and then mounting our view application to it. The difference now rather than passing in an object, is this new app file can also contain components, but ultimately they all nested under this one App.vue file. After this, we have the gitignore file. If you are using git for version control, we have a file to say which files we don't want to include in gits, followed by a babel config file, which we can use to add any custom babel settings. Babel is a tool used to transform any newer JavaScript code which writes into the older style syntax. If the browser does not yet support it, the package.json is a config file which we have in node based projects. It contains project information such as the name and also any scripts which we're going to use to start our project and also build for production. We've already used this serve script when we ran MPM run serve, startup our development server. Dependencies just below are the main packages our project needs and we can't add more at a later date too. We'll link it to Vue.js package rather than using a CDM link. Dev dependencies are packages which we only need during development, such as the development server to run the project and also things such as eslint, which will check our code quality inside the editor during development. These will not be included when we push our site to production and we'll have a lock file which we don't touch. Mine is a yarn version, but yours may not be if you were just using MPM. This is a auto-generated file which lists all of the packages which we use, along with all of the dependencies on the exact version numbers we have installed. Finally, one quick note too, we only have a small amount of dependencies and dev dependencies inside a here. However, this resulted in a lot more Node modules. This is because some of these packages also have sub dependencies too. So further packages are also pulled in with MPM. But now this is an overview of our course project and we'll get a lot more familiar as we create our next project.
29. The Vetur Extension: In the previous video, this App.vue file, amongst others, had some nice color-coded syntax highlighting inside of our text editor. But your version may be pretty plain, just like we see here. The syntax highlighting, amongst other features, was created inside of Visual Studio Code by a new extension. If you are using Visual Studio Code, I would recommend you also install this too. To do this, go into the Extensions tab. Then we can search for an extension called Vetur. Select this, and you will need to install this and enable. I already have this installed, so I'm going to click on the Enable button. Then we can go back into our App.vue. Vetur now gives us this nice syntax highlighting to make our code much more readable, along with some formatting and auto-completion too. If you are using Visual Studio Code, I would recommend you install this extension, but much better developer experience.
30. Section Intro- Countdown Project: These upcoming sections are going to focus on creating a new project, which is an event countdown application. We're going to be using the most sophisticated Vue CLI setup, and also learning about how to pass data between components, and also a lot more techniques which Vue has to offer, such as watchers, computed properties, and so much more. Let's jump in to the next video, where we're going to download the starter, and then we can begin to build our project.
31. Starter Project Download: For this project, I have provided a starter template which is available to download from the GitHub repository. Early on, when we downloaded the full repository, mine saved on the desktop as a vue-3-course-master. Inside here, you will see a lot more folders. For the one you were interested in is a vue-countdown- starter. Let's drag this over into Visual Studio code. I'm going to begin to work with our new project. Using the starter project is exactly the same as the vue-CLI-setup, which we looked at in the previous section. But using a starter project means we all on the same version numbers. I will also update this when it's safe to do so. Inside a Visual Studio code, you will see all the familiar files and folders from the previous demo. The only difference is if we go to the App.vue file. Inside of our script, we have this event data array. This is an array of objects which we're going to use as a starting point for this project. This is just going to, say, I was typing out some different events, so we can see this information in the browser. But very soon, we will create these events. We'll update them and also remove them from our projects. If you want, you can also change any of this event information, such as the event names, the details, all the background colors. But one thing to note is these dates. Very soon, we're going to need some event date which in the past or have expired. We're going to need some in the future and we're going to need some for the present day too. This will just allow us to test things out when we get to that stage, but we'll come back to this very soon. Also to keep the download size down, I've also removed the node modules. We'll need to go over to the terminal and also install this too. If you use any built-in at terminal inside a Visual Studio code, you'd go to Terminal, New Terminal, and this would be opened up inside of your project folder. If you use any separate terminal, you will need to use the CD command, which is going to navigate to our project directory. Mine is saved on the desktop. To begin it, let's CD into the repository folder, which is the vue-3-course-master. Then from here, you will CD into the view Countdown Starter. Make sure you are in the correct folder. Then we're going to install the node modules with npm i. Npm i is short for installed. This will pull in all of the packages which we need for this project. Once this is done, we can start up the development server with npm run serve. then this is going to open up our project on local-host. If we Command or Control click, this will open this up inside of the browser. While developing this project. We can leave this development server running. But if we want to close this down, all we need to do is to press Control-C, which will close down our development server so we can no longer access our site. Then to restart things, we then run npm run serve once again. We can now access our project once again in the browser.
32. Single File Components: Earlier when we looked at components, we had a preview of what we're going to build now, and also the components structure too. Using this view CLI setup, we can create these components in their own file to keep them organized using the single file components method. This is possible since the CLI uses tools such as Web Park to bundle them all up for production. So we are not penalized for having lots of files to download for the user. On things to note is we do have a components folder, but we can create components anywhere inside of this source folder. But generally we group them together into the components folder, so it's more organized. Now let's create our first component for this project, which is going to be the Event.vue. As a naming convention, we generally start component names with a capital letter. These needs have the.vue extension. This Event.vue is going to be the component which we are going to use for each one of the events listed. Here, we can set up the components just like we've seen earlier with our free sections. Have the template section. We have the script section with our export defaults. Then finally at the bottom of the styles section. This works just like earlier when we looked at global and local components. We move over the template section of our HTML. This is much more convenient now since we can just write it like a regular HTML file, rather than including a this as a string. Our JavaScript goes into the scripts action, include our data, our methods, props, and others too. We then wrap this in a export default, so we can import all components into other files. The style section, it can be wrote just like a regular style sheet. The CSS we write applies to all of our application, not just the template inside of this file. However, if you do want to restrict this to only apply to the single file, we have the scoped attributes. Also, if we want to use other languages too, we can add this as a Lang attributes, within appropriate loader. Put more on this later. See our component. We first need to add some content inside of the template. Now, let's go to our Event.vue and simply add AP elements with the text of hi. At the moment, our event component is just a regular Vue file. To see it, we need to place it exactly where we want it to appear. Remember from early we hard at this App.vue file, which is the main Vue.js file, which is mounted to the DOM. So meaning everything starts from here. This is going to be the place where we can add and register, our component to display. First of all, we could remove the HelloWorld components. We can also remove this from the components folder. Then we can register our new Event.vue file. Scroll down to just below this sample object. Here we see we are still trying to import the HelloWorld example. Where instead we want to import our event, which is also inside of the components folder. We then need to register this component to use in our template. To do this, we pass in our imported name into the components object. With this now registered, we can go up to the template. Then I'll put this in between a template tags. We can even do this just like a HTML element with the opening and closing tag. Or we can also use a self-closing tag, just like we would do with an image. Save this and we see the text of high inside the browser. This is another one of the benefits of using the Vue CLI setup. By default, it includes hot reloading, so if we save the project, it will be instantly updated in the browser. We obviously don't want this message of hi, so instead we're going to go over to our Event.vue components, and replaces with our event data. Each one of our events is going to be surrounded in an article, so we can change this to an article elements. Next, a div which is going to have the class of data. Then alongside this we're going to have a second div, and this is going to have the class of countdown. This will give us the structure which we need for our events. The data section is going to contain the name and also the details for our events. Then the countdown section below is going to be the section on the right-hand side, which will count down the number of days for this events. So first inside of the data, the level three heading, which is going to be for the title. For now, we can just hard code in any event inside of here, such as a graduation, a class of name. Which will use NCSS, very soon. AP elements, which is going to be the details. Then we can hard-code some text for our details. This is going to be all of the structure for the data section, so down to the countdown. This will be split up into two sections: The first is going to be a div, which will contain a button. This button will simply output a cross so we can delete the event. The wrapper will have a class of remove_btn_wrapper, then we can place our button nested inside. To give us the cross we can add a h small entity, which is the ampersand, the hash, and then the value of 10060. This would a semicolon and a button class, but I will style in which is going to be remove_btn. We'll add some styling soon, so this data section is on the left and then this countdown section is on the right. For now though, if we go below this remove_btn_wrapper, the next thing is to add AP elements, which will be the number of days left. For now this would be a hard coded value of 56, a break tag, and then inside of the small elements, we can have the texts of days left. Scrolling down for now, all we need to do inside of our scripts action, is to give this components a name. Given a component named couldn't help with debugging, since the component name can show up alongside messages, and also in the Vue developer tools too. Then finally I'll style section at the bottom, and this is also going to have the sculpt attributes. So these styles only apply to this template. From the very top, the whole event is surrounded in the article elements. The colors will be dynamic soon, but for now we can set a background color of light slate gray. This will also make use of the flexbox for layout so display flex. Then this will allow us to use a line items and then censor our content. The border radius of one rem. Since there will be multiple events, we can also add some margin on the outside of this article of one rem on top and bottom and zero on the left and right. Some padding, which is the space in, inside of the event of 0.2 rems on the top and bottom. Then a larger value of one rem on the left and right. The text color solely stands out on the darker backgrounds of white smoke. Finally the font weight of 300. Next up we have nested inside the data section and also the countdown section. These are going to make use of the flexbox so we can add some space in. To do this, we can apply a flex value first of all to the data. It should be a value of three. Then we can move down to the countdown section. If we set the flex value inside here to be one, the data section will now take up three times the space of the countdown section. After the downtown, the text-align to be in the center, the font size of 1.6 rems. Then a board on the left-hand side, which is going to split up our two sections. We can do this with a border left, which will be one pixel wide and a solid line. After this, let's set the font size for our text of name and also details. We only name sections be little larger so we can set the font size to be 1.8 rems. Then the details section, which will be a little smaller, so let's go for a font size of 1.6. The last section is to take care of is this remove button and also the text. So first of all, let's go through the actual button. If we scroll up, we can see this has a class of remove_btn. A transparent background to remove the white background color. Set the border wide button none. We can also change the cursor to be a pointer. When the user hovers over this button. Finally we can push this button over to the right-hand side, by setting the text line on the wrapper. The wrapper had the class of remove_btn_wrapper. The only CSS property is to set the text line to be right. This now gives us the base which we need for our event. Now we can use a for loop, to loop through this event and make the content more dynamic.
33. Making Our Events Dynamic: Included with this course inside of the App.vue file, I have provided an array of objects. This is our eventData which we're now going to use to loop through and provide these as props to our event component. Currently this is commented out since if we have a variable like this and we don't use it, the ESLint plugin which we use in, will throw an error. Now, we're going to use this. We can select all of this and uncomment all of this out. We can do this all at the same time with highlight and then a command or control alongside the forward slash. One thing to watch out for here is we can't place in any JavaScript code we want inside of this script section, but Soul Vue recognizes it, it does also needs to be present inside of the export default. So we now need to create a date property and then assign this array to it. Create this data section and then inside of the returns section, we're going to create a date property called events. This is going to be equal to our array, which is eventData. Vue is now aware of this eventData since it's now stored inside of events. We can now go up to our template and we're going to loop through all of these events. Then it creates a new component for each one. For this, we'll create an unordered list and create a list item to output each one of our events. This list item is going to be repeated, so we can use v-for. We will say "event in events." Also, this needs a dynamic key behind it. We have a unique value for each one, which is this ID. Next, we can move our events components inside of this list item where we are going to pass our event as a prop. Use a colon since this takes in some dynamic data, and we're going to set this name to be event, which will be equal to our event variable from the loop. Save this and instantly we'll see we have six different events inside the browser. Since we have six events inside of our event data, we do, however, all have the same information, all have the same name and also the same description, and the same date to. To fix this, we're going to go over to our Event.vue component. The first step is to receive this data as props. Props is an array which takes in our strings, and only prop which we are receiving is the event information. We can now use this up in our template. We have the event name, so open up the double curly braces, and then replaces with the event.name. That's our name now taken care of. Then at the detail section, remove this. This was the event.details. Good. This is all working, but these all still have the same background color. If we go to our eventData, we can see that a different background color has been provided for each one of these events. We can now make use of this over in our event component by passing this as a dynamic style property and use this to set the background. Inside of the opening article we can use the colon to bind a dynamic style section. Remember, our styles are passed in as an object, so we need to open up the curly braces. Inside here we're going to set the background to be equal to our event.background. Good. This just leaves us with the number of days left which we are going to come back to since we don't need to perform a calculation from the eventDate. I will also fix the color contrast too for backgrounds such as this yellow, where we can't see the text which is been overlaid. To finish this off, we're just going to add a little bit of CSS, first of all, to our list into the app section and scroll down to the styling section at the bottom. The events are surrounded in the unordered list, which we're going to set to have a zero-padding to remove any browser defaults. Then each one of these events are stored in the list item. So we'll set the list-style to be none Then finally, the cursor is going to be a pointer each time I hover over any one of these list items. To also restrict the maximum width of these events, if we stretch the browser, we're going to now set the maximum width inside of this app wrapper. This app is the wrapper for our whole application, and we can see this if we go into our index.html. This is the section just here where our application gets mounted to. This will apply globally. All I'm going to do in here is to set the maximum width to be 600 pixels. Then we can center this with margin zero auto. Zero will set the margin on the very top and bottom and then auto will apply all of the margin, which is available equally on the left and right.
34. Calculating Days Left: For each one of our events, we need to calculate the number of days left until the event occurs. To do this, we're going to create a calculation based off the current date and then the date inside of our objects, so this property inside of each one of our items. Once you have this and the current dates, we can then work out the difference between these two dates and then convert this to be the number of days. This inside of the [inaudible] we are going to create a method. Under the data section, we can create our method section, and the name of the method is going to be Days Left. Days left is going to take in the particular event we want to calculate. This is soon going to calculate the number of days left. But first we need a way of actually calling this method and also providing the event data. Well, we cannot do this all at once in our template. We can setup a property which is going to be passed to our events. This will be dynamic, and this is going to be the number of days left. This can be set to old method, which is also called Days Left, where we're going to pass in each individual event, which is from our loop. One thing to beware of here, we are not actually passing this method as a prop. Instead, what we're going to be doing is passing the result of this method, which is going to be returned back. The way we can do this is by calling this function and then providing a return section at the end. We're going to return value rather than the actual reference to this method. Let's first check our method is working and we are receiving the event data by doing a console log. Let's do the event.date and for this we're going to need to go into the browser developer tools with right-click and inspect, open up the console and now we see six different dates since we are calling this method for each one of our events, mainly this is working fine. Now we need to work with some JavaScript date methods, and to get the current date, we can use date.now. First let's do this inside of a console log and we can check we have all the information which we need to log data now. Then we're can refresh the browser, and this may look pretty confusing, but we need to look at each one of these impairs. We're doing it two console logs for each time the method is called. The first two is for the first event, the third, fourth item is for the second event and so on. We can see that both the event.date, which is the first one on the date.now is in completely different formats. The first one of the event date is a string, which is the year, the month, and finally the day and then below the current date. The current date is going to be formatted in a number of milliseconds since the 1st of January, 1970. To compare these two, we need to convert the event date to be in the same format, and to do this JavaScript provides us with a date method called Pass, upon is to replace, again we'll access the date object and call the method Call Pass. Where we're going to pass in our event.date, refresh the browser and we'll now see that each pair of dates we have is now in the same format. This is good because this now means we can compare our two dates which we have. We need to build this up in a number of stages. The first stage is to deduct the current date from the date of the event. Grab this and then we'll deduct date.now. This will give us the difference which I'm going to store in a constant called Time. Just like the results within inside the console this time, variable is also going to be stored in milliseconds. This time constant is stored in milliseconds, so we need to now convert this. Grab all constants and to convert milliseconds to seconds, we divide the result by 1000, which will then give us the number of seconds between the current date and the event date. But we don't want the number of seconds. What we want to do is to convert this into the number of days. To convert this seconds days, first, we need to multiply this by 60 seconds and then again, we'll multiply this by 60 minutes and last of all, we'll multiply this by 24 since we have 24 hours in a day. This will be our new results which we can store inside of a constant called Days and make sure that the text editor doesn't place these brackets in the wrong places. We need to provide the brackets around the numbers. Just as a recap, we have the time in milliseconds. To convert this into seconds, we divide this by 1000. We multiply this by the number of seconds in a day, the number of minutes in a day, and then the number of hours in a day, which results in the number of days between our current date and also the event date. We can take a step out of these two by reducing 60 multiplied by 60 by instead of passing in the result of 3,600. This is exactly the same, but just a shorter way of doing things. The final thing to do to get our correctly formatted number of days is to round this number up. Cut out the result of this, use a math function called Seal, which is going to round up our number to the nearest whole number. Passes [inaudible]. We can clean up our console logs and it's this number of days which we want to return back from this method. It is return value which is going to be passed as a prop to the event component. Let's head over to this event component now, we can receive this as a prop inside of our array. This prop was days left, then we can check if this is working by outputting this inside of our template. Instead of this hard-coded value, let's remove this and output prop which was days left. Over to the browser and this has now changed for each one of our events. Notice he will see both positive and negative numbers and this is completely normal and this is something which we will deal with soon.
35. Conditional Rendering: Earlier when we first looked at this event data array, I recommended that you set some of the dates in these objects be in the past and some in the future. If you've not yet done this, now will be the time to do it. This is what I would like you to do: set at least one date to be in the past, one to be with today's date, one to be with the date tomorrow, and another with any of a future date. This will help with testing things out very soon. To do this, we're going to make use of conditional rendering, which is a way of only showing something, such as our event, when a certain condition is met. In our case, the condition is going to be the number of days left, which we have setup from the last video inside of this method. If this returns a positive number, this event is still yet to take place. However, if it returns a negative number, we know this date has passed. Meaning we can hide this from the user inside the browser. To do this we're going to go over to our event.view and then open inside of the opening article section. We can show and hide these events by using a view directive called v-show.v-show is another directive and will only show this element if this condition is true. For example, if 10 is greater than 1, this is now a true and our events will now display. If we say if 10 is equal to 1, this is not true, so this will now remove our events. But instead what we need to do is to look at this days left prop and decide if it's a positive or negative number. To d this, we can use a JavaScript function called Math.sign. This will allow us to pass in any number and get back either a negative one, a positive one or a zero. We can see here with the examples, if we pass in the value of three, will get an output of one. If it's negative, we'll get the value of negative one. If it's zero, we get the value of zero. It doesn't care the value of the number which we're passing, we're only ever going to get back a one, a negative one, or a zero. This is going to be perfect for using in our v-show let's pass in Math.sign and then our days left prop. We can now check if this is equal to negative one or to the project. The results which you see back here will depend on the date which you've set for each one of your events. I currently only have one event which is in the past, so I only see one in the browser. To see the events which is still yet to happen, we can change this to be a positive one. But with these two options, we've only seen five out of our six events. This is because we have set one of these events to be today's date, meaning that there will be zero days left. To deal with this, we're going to set this to be not equal to negative one, which will filter out all of the expiry dates and only leave us with the date which are either today or any in the future. The next thing to take care of is the text, which is the days left. If the number of days left is zero, it should read today, and for any past events, it should read days ago. We can, again, make use of the days left prop, which is eventReceive, and combine it with some more view directives called v-if and v-else. As they sound, these work just like JavaScript if-else statements. We provide two sections of code called if and else. If the condition is true, that section will run. If it's false, the else section that will run instead. Down at the bottom of this event template, we have this p elements, which I'll put to this section just above. Let's deal with today's events first. Open up the p elements and we'll set the text to be today. So what condition do we want to display this element? Well, we're going to check if the number of days left is equal to the value of zero. We can then combine this with a v-if directive and check if the number of days left is equal to zero. If this is true, this is going to render the text of today for this particular event. Save this, and we only have one event which is today, and we see this text inside here. This now leaves our two p elements alongside each other, but we only want to show one or the other. For this, we can add the v-else directive to the next p element. Save this, and now we only ever see one of these sections depending on the number of days left. It's one thing to be aware of when using v-if and v-else, the v-else section must immediately follow the v-if element, so we couldn't have anything in between such as a span. As we can see, this will cause an error. This now means the first element of today will show if the number of days is zero. If the event is in the past or the future, the second p element will display. Inside of this second p elements, there is some further work which we need to do to show the correct words. First of all, inside the small section, we have this word of days. This word should be changed to the word day if only one day is left and the JavaScript ternary operator is going to help with this. Cut this out, open up the curly braces. We can check if the number of days left is equal to one. If it's true, we'll output the string of day. If not, we'll say days. This is a question mark. We save this is all in our work and we see one day in the browser, 59 days and a 144 days too. We also need to do a similar thing for this word of "left". To see this, if we go through this off and temporarily change this to be positive one, any events which have expired, the text should read "days ago." Let's remove the word of "left", open up the double curly braces, and then we're going to do a similar thing too up at the where we use Math.sign to check if this is a positive or negative number, as in the days left. Then we're going to check if the returned number is not equal to negative one. This will return true if the date is in the future, and we can output the text which is going to be "left". Or alternatively the text of "ago". Over to the browser the section now works, we see "12 days ago". Change this to be negative one. For future events we see days left. Something else which we need to take care of is if we change this to be a positive number, for any events which have expired, we still see negative 12. This should be simplified to just say "12 days ago." For this, we can use another JavaScript math function, which is called absolute. If we go back to the Mozilla website and down into the method section, we have this Maths.abs. Down to the example section, we can see regardless of if we pass in a negative number or any positive number, this will always return the absolute value without the negative symbol and this will be ideal for our use case. Back over to our template and where we output the number of days left. We're going to now pass this into Math.abs, save this, and now our negative number should be removed. Let's just check the positive numbers are working fine and this is all now working too.
36. More on conditional rendering: Up to now, we've used v-show, v-if, and v-else to show and hide our elements. But what is the difference between them? Let's take a look at this and some other things we can do with conditional rendering. Here is a simple example which could be used on a banking app or similar, which asks the user to change the password after a number of days. This simple example would be no good if the user changed the password yesterday or even 10 minutes ago. We would use conditional renderings that only show this message if the user has not been logged in for say, more than 100 days. This would work, but the v-for directive has been repeated. Maybe not a big problem for this small example. What if a full section relied on this condition who needs to add this directive to a lot of elements? Another way is to group all of these elements together inside of a template tag. We could then add the v-if directive to this wrapper. Meaning, it would apply to everything inside. This template could also be a div, a section or any of the elements. For the benefit of using the template is that it will not be rendered inside the browser. The browser will read the code just like we see at the bottom. It acts like an invisible wrapper so we will not interfere with the rest of our code, optimize any unnecessary rapid divs. In the previous video, we looked at using v-if and v-else. But what if we had a third condition? That is, we could also make use of v-else-if. In this more complex example, we welcome back the user if the number of days since the last login is less than 50. Then v-else-if can be attached it to the next single elements or a group of elements using the template wrapper. The section checks if the number of days since the last login is between 50 and 99, and then will ask the user to reset their password soon. Then as a final section, we can use the else directive, and it will run if the above two conditions have not been met. Meaning, this will run if the user has not logged in for hundred days or more. We will then ask them to reset their password. We could also use multiple v-else-if sections too, and this is if we have more than three conditions we want to check just like we can do with regular JavaScript. We can use as many of these as we want as long as each one of these elements follow on from each other. We've now seen both v-show and v-if as different ways of conditionally rendering elements. But why does vue have both ways when they appear to do the same thing on the surface? Well, underneath showing and hiding things, there is some differences. The show elements are always rendered to the DOM. Even if they are not visible, they are still there. It's using the CSS display property under the hoods to show and hide. It cannot be used in combination with template elements as a wrapper, nor going to be used in combination with v-else. Since this directive is always present in the DOM and the CSS display property is used to toggle, it's often a good choice if we expect it to toggle on a regular basis. V-if is more of a real conditional rendering. Unlike v-show, v-if is not always present in the DOM, instead, it only renders if the condition is set to true. If a condition becomes false, all of the section and controls will be completely destroyed including any event listeners or child components too. Since there is a slightly heavier cost of completely add-in or moving the sections, it can be beneficial to use when we expect the condition to change less frequently. All of them have needs to completely unmount it from the DOM.
37. Computed Properties: We recently created this daysLeft section, which has quite a bit of JavaScript logic nested inside. Even though all of this code worked perfectly fine, things start to get a little messy. It can be generally recommended to extract more complex logic away from our templates whenever possible, leaving the template closer to the HTML, which it was intended for. Help with this, we can't outsource this logic into our script section as a method. Let's do this. Well, the props. Preview methods and the method name I'm going to give this is daysLeft string. This is going to be responsible for returning a string which will replace all of the text we see over on the right-hand side. It will say daysLeft, days ago, depending on the number of days left. For this, we can reuse most of the section in between these small tags, so copy the two lines of code inside of the curly braces. This can now be used as a starting point for our method. We now no longer in our template, so we need to remove the double curly braces for each one of these sections. Also, since we are now referring to daysLeft, and this path of our Vue instance. We need to refer to this with this.daysLeft in both of our lines of code. Then store each one of these lines in a constant. This is going to be either the day or the word days. Don't want to call my constant day or days. So this equal to this value. Next, a second constant, which will be either the word left or go. Then we can return the value of both of these words combined. We need to return something such as days ago or daysLeft. We can do this by returning the value. Then we need to add together both of these constants and return these from the method, so this would be day or days plus the value of left or go. We can now call this method in place of where we cut out these two lines of code. In between the small sections, we open up the double curly braces, and then it call our method. Since we actually want to run this function, we call it using the brackets. This will be replaced with the new string value, which is going to be returned from our method. This leaves us with a much cleaner templates, and we can also reuse the code elsewhere if needed to. Let's test this. You see one day left, that's all we're confined, and change this to a positive number. This should now say days ago, the greatest space in between these, all we need to do is just to add a space inside of our string. After day or days, and a space and this will be shown in the browser. This is good but not always the best solution for all cases, since a method needs to be called manually, just like we are doing here. The method is only going to be called once the component loads. If a day passes and the number of days changes, the UI would not be updated with the new value, until we either refresh the page or call this method manually. The situations like this, we could use a computed property. Computed is another section which is possible options API, which we've been using so far with the data and the methods sections. The structure is exactly the same as our method section. So all we need to do is to change this to be computed. I'm going to see straight away this causes an error, which is because unlike a method we don't need to call this using the brackets. All we need to do is to provide a reference that is computed property and it still works exactly the same. A method and a computed property looks the same on the surface. We call a method manually, but a computed property will automatically update the template if any changes happen inside of here. We see currently inside of here we have some reactive data, which is this.daysLeft. If a computed property contains reactive data just like this, or any other data property or methods. Each time that data updates, it will automatically update the template. Meaning that if daysLeft changes, our template would automatically change to. These computed properties are also very efficient, they are cached, and will only re-evaluate if something reactive changes. If nothing inside of here changes, [inaudible] this computed property would result in the cached results returned, making it very efficient to use. One more example of a computed property would be to change the color contrast between our event backgrounds and our text. If we return this back to a negative one, the yellow background is hard to read, and also a little bit for the green too. For the events which have these two background sets, we're going to darken the text color. This will also be a computed property so just after our daysLeft string, the second computed property is going to be called changeContrast. This is our [inaudible] just like a method and it just like our previous computed section. The first step is to set up a constant which is going to be an array with our two background colors. I'll call this the low contrast backgrounds. If you do change any of the colors to use with your events, it can also change the values inside of here too. The yellow and the green values are going to be f9f970, and then the second array item is 68ee94. Don't worry if you see an error at this point. This just means that we've created this array, but we have not yet used it. Next, we need to check if this current event has any one of these two background set, we have the event passed as a prop. Each one of these events has a background property. Meaning you can now check if our event.background includes any one of these array colors. The way to do this is to access a JavaScript array method called includes. Way to do this is to access our array which is low contrast backgrounds, and then call a JavaScript method called includes. This will land grab all of the values inside of here, and check if includes a particular value. A particular value we want to check is the event.background since this is also inside of our view instance, we use this keyword before calling event.background. A computed property must always return a new value. This new value will be either true or false, depending on if this color is included as the event background. Thus we can call this changeContrast computed property. Inside of the double curly braces or places just below our closing article. This, and over to the browser, so the yellow one is a little hard to see, but really select all. We can see that the yellow one is true. We how false for the other colors. This green one is also shown false so let's see what's going on here. This is 68ee94, we can just compare this. This was the conference talk, I I think this is just because of the capital letters. So let's change this, making this an exact match. Now this is set to two as expected. We don't just want to output the value of true or false so in our template. A better use would be to only apply a dark text color if this results in true. We've already looked at how to do this by using a binded style attributes. This is already settled for each one of our articles separated by a comma. We can also set the CSS color property, depending on the value of changeContrast. If changeContrast is going to be equal to two, we'll make use of the JavaScript ternary operator, the toggle between two different colors. If this is true, the color we want to place is a hex value of 45 before it will fall. If this is false, meaning we are not on the low contrast backgrounds. We can then set the font color to be the value of white smoke. Save and the text color has now been updated for the yellow and also the green background. The benefits of using a computed property here is that later, we will be allowing the user to edit each one of these events, including the background color, meaning it will watch for any changes to the background, and then update the color as needed.
38. Watchers & Multiple Root Nodes (fragments): Alongside computer properties, UJS also provides us with another way of watching for data changes. Then running some code after this has happened, this is by using a watcher. To do this, we need to add the watch property to our options object inside the App.Vue file, alongside our data and our methods. We can place in our watch section as an object. Inside here we're going to pass in a callback function, which runs when any of the data which we watch and will change. We can watch when a data property, a prop or even a computed property changes by naming the callback function at the value which you want to watch. So for example, if we had a data property called darkModeSet, which was initially set to false, we would then name our watcher after this property which we are observing. So for this one, this will be darkModeSet, and then we would run some code inside. So you better see this example, we can also add a button which is going to toggle this between true and false. Open the template at the very top, set up button with a at click event listener, which is going to set darkModeSet to be equal to the opposite, and then I'll put the value just below to check this is working. I should now be able to toggle list between true and false. Another thing which we done here is also taken advantage of another Vue free feature, which is multiple root nodes, also referred to as fragments. Here inside of our template, we have placed a button alongside our unordered list. So effectively we have two roots or parent nodes. Previously when using Vue 2, we would need to wrap all of this content into a single parent elements. So for example, would need to use a div or a similar wrapper, and then place our code inside. This is no longer required in Vue version 3, and it avoids adding unnecessary wrappers into our HTML. Back down to our watcher which was setup, we can also pass to our watcher the value, which is going to be the value after the data has changed. I want to name this as val, but this can be anything which you prefer. We can see this with a console log and then open up the Developer Tools. We don't have anything showing in the browser, but if we click on the toggle button, this will then reflect the data after this has changed. So this is now true, click on it again. This is now changed to false. This now gives us the opportunity to run any code you want after something is changed. It also gives us access to the new value. But what if we wanted to also access the old value before the change occurred. This we can pass in two parameters. Again, we names of our choice. So the first one can be the new value, then the second, the old value. Place both of these inside of our console log. Open up the Developer Tools. Now if we click "Toggle", this will now give us access to the new and also the old values. So this is how a watcher works at most simple, and this is often enough to cover so many used cases. But there's also a few more options which has available and also some Edge cases too. [inaudible] want to watch an object or an array, let's see this by changing this to watch our events array. Now we can try to add a new event and see what happens when we try to update this. So back up to our template, and this time we're going to access our events. Then can push onto the end a new event. We can grab any one of these events as a template, paste this in and just change some of the details, it's going to be number seven. Since we're inside of these double quotations, we need to know use the single quotations inside of this object. So go through each one of these and change the double quotes to be single. Then once we're finished with this, we can now all put the value of events. Save this file. We see we have all of the values from one through to six. Click on a button. Our seven object is now being added. Open up the Developer Tools, hit "Refresh". Let's see what happens with our watcher. Click on "Toggle". Now our seventh event has been added, but we don't see anything inside the console. Even though we are still watching out for the old and the new events, and also trying to log this to the console just here, This now means our watcher appears to be not reacting to any changes. This happens when we are watching an array or an object. If you're familiar with how JavaScript handles primitive and reference types, this will make more sense at moment. If not, this is covered in a later reactivity video. For now though, you will need to understand that when we refer to an object or an array, any changes to the elements or the object properties nest inside will not trigger an update. For this watcher to be triggered, we would need to replace the full array or object, rather than just one of the properties which we have inside. Or alternatively, we can pass in a deep option which will tell Vue just to also watch for any changes which are nested inside. To passing this deep object, we need to also change the way which we construct our watcher. Rather than setting up a callback function like we have here, we need to also change this to be an object. This object now means we're still watching out for any of these events. We can now pass in some of the options. The first property is this deep option, which we're going to set to true. Separated by comma, we then add in our handler function, which is going to run anytime this event data changes, and it's inside of this handler method, which is going to replace our callback function. So just like with all this, can also take in the value which you can log to the console, refresh and open up the Developer Tools. Now when we add our extra events, this is now reflected inside the console. Open this up and click on the array section, and we have seven different objects. Also notice here inside of our watcher, we only passing in to the handler a single parameter. This is again due to the fact that we are watching a object or an array. We now only have access to the new value, rather than the old value. Another property which we can add to this watcher is Immediate. The Immediate option will allow us to run the watcher as soon as our component loads, along with any of the changes too. So currently, let me refresh the browser. We see that this watcher is not run immediately until we make any changes to this data. If we did want this watcher to also run as soon as the component loads, we pass in the Immediate option, and then we're going to set this to true. Test this out, open the browser, refresh. We see that the watcher is run immediately, and also may click on the "Toggle" button too. There is quite a bit going on here and you may be wondering why Vue provides us with both the watcher and computed options. When the both appear to do similar things on the surface. Well, there is actually some key differences between these two options, which we're going to discover next.
39. Computed or Watchers?: When first starting off with Vue JS, it maybe confusing why both the computed property and the watcher both exist when they both appear to do a similar thing on what you need for a change and then running some code. But there are some key differences and one of these is if you want to watch for multiple pieces of data. The View documentation shows this great example where we have the first name and last name. We wouldn't need to create two watches for the first and last name to construct the full name which we need. In this case, a computed property would be better since we can reference multiple pieces of reactive data and it will run if any of these changes. Considering this exact same example, notice here we're taking in some data and then return it a new value. As we know, this value can be accessed anywhere in our template by using the computed name of create full-name. This is perfectly valid with a computed property on the correct way to use it. But we should not try to use them to change any external data or state, such as anything inside of our data properties, as it results in what is called a side effect, which will then throw an error. The key here is to use a computed property when we want to observe a data change and then return a new value. Then if we want to update our data, or our state, this should then be done using a watcher. To summarize, we can use a watch property when we want to run a function when data changes. But instead, if we want to return a new value which is calculated on some existing data, we would then use a computed value. Also computed allows us to watch multiple data sources, much more efficiently too. As we looked at in the last slide, we should not use a computed property to directly mutate state, nor should we use it for any asynchronous operations. Meaning, if we had an asynchronous tasks such as corn of some API data, which may take some time to come back from the server. A watcher is the way to go. As we've already seen, computed properties are also directly accessible from within the template. So we can use our output, the new return value. Another difference is not computed properties are also cached. Imagine a date property is being observed by both a computed under watch property? If the data property is then updated, well, the value is the same as it was previously. A computed property will not recalculate since the value has not changed. However, the watch property will still run the callback function, regardless. As you can see, both have their uses and advantages, both for different use cases.
40. Ordering & Toggling Past Events: Inside of our event.vue up at the very top of the template, we've been manually switching between events in the past and future by changing this-show sat in. But now we're going to change this by adding a button, do this follows. Along with this, we also going to add a computed property, we'll arrange these events into date order to. Just before we do this, we can also remove the data from the previous watches video. Since we don't need this for our project, we don't need the watch section, this can be removed, the dark mode set property, and also the section from our template which was the button, and also the events output. So within this app.vue section, let's start with the toggle button by adding a new data property which is going to be called showPastEvents. This is going to be a Boolean value which will be initially set to false, and then open the template. We can also pass this to our event components. Make sure this is also dynamic too, so we're going to use the colon for binding the proper name of showPastEvents, which will also be equal to the value with the same name. Since this data is a property, also need to go over to our event.vue and pass this into our props. This prop can now also be used up inside of our template to show and hide each one of these events, and inside of the v-show, directly we'll pass in the O operator. It also shows event if our showPastEvents prop is equal to true. We currently have this set to false so let's toggle this down in the data section. If we make this true, refresh the browser and now all previous events will also display to, it's all in our work. So now we need to introduce a button which is going to switch this for us inside of the app.vue template and just above our unordered list, so this appears at the top of our application. Creates a new div with the class of options. This will be the wrapper for our button alongside some other buttons which we'll add later. The button is going to listen out for a click event where we are going to toggle the value of showPastEvents. This button also needs a name so we can see this in the browser. Then over to the browser, we're going to try also our button, and this will now toggle between showing and hiding any past events. We will come back to this button later and add some styling, but now we're going to add a computed property to organize these events into date order. We can see here that this one which is today should be at the top, we should then see the one day left, 59. So these events are not currently in order. This app.vue file contains all of our events which are stored in this data property. Instead of now looping through these directly, first, we're going to setup a computed property which is going to rearrange these into the correct order and then return the newly arranged events array. For this, we're going to need a computed section with a property called order events. What we want to do in this computed section is to grab all of our events and rearrange the order. As we know from previous videos, we cannot directly manipulate the state so we can't change the value inside of this data property. So instead, what we're going to do is to create a local variable which is going to be the copy of this events. So a constant called eventsToOrder. The set is equal to be a copy using this.events. Computed properties need to return something, a [inaudible] with a value of this eventsToOrder after we call the JavaScript sort method. If our events array didn't include any objects and was a lot simpler, and it was made up of things such as strings of text or even numbers, this would be all we'll need to do, since by default, the values when we use the sort method are converted to strings and will be sorted alphabetically in ascending order. Our case is a little bit more complex though. We have an array of objects and we need to sort them by the date property. To do this, we can pass into the sort method a optional compare function. This is going to compare any two elements in our array at anytime. The names we give to these two elements are completely up to us, and we can pass these in as an arrow function. So passing any variable names for our two elements to compare. I want to simply call this a and b. This is now going to loop through all of our objects inside of this array and then compare it to other time. I will have access to these two objects which are being compared with these a and b variables, meaning we can compare the dates from each one. First of all, we're going to check if the day property on object a is greater than the day property on object b. If this is true, we'll use the JavaScript ternary operator, which will return a value of one. If this is false, we'll then return the value of negative one. It's positive or negative number is going to rearrange our objects by setting the index position. So when comparing these dates, if one of them is higher, it will then be pushed up the index position with a positive number. If not, it'll be pushed down with the negative number, and then once it has finished sorting by comparing all of the items in our array, we'll then get back to the original array in the new order, which we can then use in our template to loop through in the place of our original events. Now we're going to grab order events up to our unordered list and then loop through this in place of our data property, save this, and instantly, our events and now updated in the correct order. Up at the very top, we have any previous events which are passed, we have today's event, and then these are now ordered from the closest to the furthest away, and they should also still work if we hide any previous events. Also, I know the benefits of how we miss in a computed property. Later on, if the user edits the date on any of these events, this will also recalculate the order to.
41. Teleport: At the moment, our application has hard-coded events. But soon, we're going to allow the user to both add new events and also edit any existing ones too. You will be doing this by adding a form, this form could be a component, and it could even be placed onto its own page. A common scenario though is to place the form into a pop up model. This means the user stays on the same page, but the form will pop up over the content. Depending on the HTML structure, modals can be tricky to position using CSS, any teleport could help with this. As an example inside of the App.vue file, by the very top of our template, imagine we had a form element just like this, in this I will just say add new events. To create this as a pop up modal, we wouldn't need to apply some CSS. Currently this form is just start at the top of the components, but generally we want a modal pop up over the rest of the content. Often we do this by setting the CSS position property to be equal to absolute, any element which is set to be absolute, will look for a parent element which is set to be relative, and then use this relative container to reference from. At the moment, this is not a huge problem since here we are inside the top level components. Well, if this form was deeply nested inside of lots of different components and lots of different wrappers, it will become a more complex CSS task. The positioning modal exactly where we want it, to help with this, the teleport feature will allow us to still keep this code inside of the template for actually render it elsewhere. Doing this provides some advantages, such as we can keep this forms HTML structure inside of the same components as the form data or its logic. It can still access anything from our view sections such as our data and our methods, but instead of being rendered inside of this component, its output in a different part can be anywhere such as another components, fulfill a modal, it may be an advantage to place this inside of the root index or HTML file. If we go into the public folder, this is contained inside of here. This index page has our main app container, this is where the rest of your application is injected. As we just mentioned, we can teleport our form to any of these nested components. Or alternatively, we could add this to the body section or can even teleport is way different elements altogether. I've had a different div with the id of modal. We could teleport this form code and places inside of this div section, meaning is completely independent from the rest of the nested structure. This would result in our form or any other section which we wanted the teleport having a left wrappers and also surrounding code to deal with, and also another benefit which is related to forms. If you know what events propagation is, you should also remove this too since it now has no elementary pass through. We now have this element to mount too, but how do we actually add this to the section? Well, to do this, we're going to cut out our form section. Then nest this inside of the teleport tags. In the opening teleport tag, we can then pass in a two attributes which is going to link to our elements. This is just like when we use a query selector or when we select an element with CSS, meaning we use the harsh for an id, and these are the id of modal. Save this and now over the browser, remember from before we added the teleport at the form, was appearing at the very top of the browser. Now if we refresh and scroll down to the very bottom, this form is now placed at the bottom of our project, meaning our teleport is taking effect. This is going to be ultimately a pop up form, so we now need a data property. We can toggle this on and off over to the data section, says obey data property, which is going to be a Boolean value called ShowForm. This will also need a button up in our template, and we can add this inside of our options wrapper, so just after the first button we'll add a second button, but our CSS, we can add the class of addNew. This is also going to listen out for a click. Just like above when we toggled the data property, we'll do exactly the same for ShowForm, and between the bottom, we'll also add a HTML entity, which will be the plus icon. The plus symbol is made up with the ampersands, the hash 43, and a semicolon. Then finally, we'll show and hide this form based on if this ShowForm data property is said to be true or false. We can do this using v-if. Save this, and now we can test this out over in the browser. You have the plus icon, click on this, see add new events, click on this again, and this has now been removed. This effect will also be more clear if we open up the browser developer tools, open up the body section. Now, inside here we can see the div with the id of app and also the div with the id of modal. These were the two sections which we have inside of our index page. Now, if we open up the modal, we see form nested inside. However, if we toggle list by clicking on a button, this now reacts to our true or false state. It is also lots of other uses too for teleport such as display messages to the user, tooltips, or just generally placing contents into other locations, so we're not restricted by our current component. In the upcoming section, we'll make use of this form which we just added to both edits and also adds new events.
42. Section Intro- Forms, Events & Modifiers: These upcoming videos are going to focus on a really important part of, not only Vue.js, but web development in general, and this is handling forms. We'll take a look at lots of things such as emitting data, backup to a parent component, validating all forms, also binding our data with Vue.js, and also some other things such as event propagation and how we can handle this with modifiers.
43. Our Form Component: Since Vue.js has this idea of creating templates in our single file components, creating a form is generally the same as with regular HTML. We need a form which is going to allow us to both add a new event and also edit any existing events too. We could either create two forms of this, or both of these operations can make use of the same form components if we wanted to. First of all, let's create a component. So I'll open up the source folder into the components, and then our new file is going to be called the AddUpdateForm; again with the.vue extension. Now we can go over to the App.vue and then we can register this file so we can use it instead of this form which we created inside of the script. Below our object we can create a new import, but this time our import is going to be importing our AddUpdateForm. Just like the event above, this is./ components. Then if we go into this, we have the AddUpdateForm. Don't forget to also register this inside of the components. Then up to the teleport at the very top of our template, so we're going to replace this form section. Remember this has the v-if section, so we can toggle this components on an off. We also need to bring this over to our new component, so I'll place all components inside of the modal which is also going to include the v-if conditional statements of show form. With this now in place, we no longer need this form section just above. We'll add all of the form inputs inside of our new components. Then we can start as usual with the template for our HTML, and then nest it inside a div section which is going to have the class of form_ wrapper. We can then place our form, nest it inside. The reason we have this form_ wrapper surrounding our form is because it's going to help us out later on when we style this modal. We're going to have this form rapper as a darker background, so it covers all the rest of the content when this modal is active. Then this form section will have a lighter background so this will overlay this div. The first section is going to be surrounded in a div, and this is going to be for our form input of name. This will be the event name such as graduation. First of all, the label which is going to be for the name section, and this will have the label of name. The input, the type of text. The ID for this is going to be name which matches our four attributes. The next one, if we check out our object, this one is going to be for the details and we need one for the date and then one for the background color. Let's copy this div section since this next one is also going to be the input with the type of text, but this time this is going to be for the details; the label too of details and also the matching ID. Onto the browser we can check this is working by clicking on our button and scroll down, and we have our two form inputs. The next one just below this is going to be for our date service, we'll have the input with the type of date. This will give us a date picker to select the date of the event. This label is also date too, and also the text for the label too. Now this just leaves the background color, and for this we have six different options for our six example events. I'm going to make use of the same six colors which we have used inside of here. To choose between any of these, we're going to use a select input. This will also have a div wrapper to keep the styling consistent, also the label for the background. But this time this will have the HTML select inputs. Select has a opening and a closing tag so we can add all of the options which we need to select in-between. It's ID is so much is the label of background, and then we have our six different options which we are going to include and the first value is the hex color of F34949. This is for our red option. The second option was for our pink color and this has the hex value of F07949, and the text of pink. The third option is going to be for the purple value, which has the hex color of 997AF0. For the blue, this is 7AD3F0. Next we'll add a green option, and the green color I'm going to use is 68EE94. The option for the yellow, this is going to be a value of F9F970. Finally, we'll also add an option for the orange color, and this hex value is going to be EB980F; any text of orange. This is now all of the options which we need and to finish off all form we'll just place a div at the bottom with the button, so we can add a new event. But now this button isn't going to do anything, we'll just add the text of add. We can go over to the browser and check this is all working, so I will toggle this on. We've got our text inputs, the date picker, so we can select a date for the event. I have the Select option with all of our different color values, and finally the button at the bottom. Very soon we'll take a look at how we can extract the values from this form input and place this into our data. But next we're going to come back to this form and add some styling, and turn it into a popup modal.
44. Styling & Positioning The Form Modal: As former created in the last video, now needs some style into not only make it look a bit nicer, but to also make this overlay all of the rest of the content. Remember, in the last video, We created this form. We also added this div as a wrapper. This is going to allow us to create a modal effect and this wrapper will stretch the full height on the full width of the page. It will have a darker background color, which is going to give us the effect of a popping up over the rest of the content. And then on top of this dark section, we'll then embed our form, which is going to be centered in the page and also have a lighter background color to make it stand out. To do this down at the bottom, we'll go blow our template, create our style section. It can also be scoped to the styles only applied to this particular component. So first of all, we'll target the Form Wrapper, which is surrounding all of our form and we'll give it this darker background color. So to give it this darker background color, were going to add a RGBA value. This is going to first allow us to create the dark color of 000. And then the fourth channel, which is the alpha, will allow us to add the opacity value of 0.7, this is similar to 70 percent capacity. And this is going to make sure that our background is going to be slightly transparent. Now this will be a darker background color, but we can still see the content in the background. Next, we're going to do some positioning so we can lay this out to be the full width and the full height of the page. First, we'll pull this out of the flow of the rest of the content by certainly position value to be absolute. Then we can use the four values of top, right, bottom, and left, which is going to stretch our form components to the four sides of the page. We can do this with the top value of zero, the right value of zero, and then also the same for the bottom and also the left. Now if we go over and open up our model, we see the darker background color has been set, but it's slightly transparent. This also stretches to all four sides of the browser. Currently though all forms placed in the top left-hand corner. To move this into the center of the page, we're going to make use of the CSS flexbox. So we'll begin with the display type of flex, and then we can set the justify content to be in the center, which will give us the alignment across the page. Now we can set the vertical alignment with align items. This is now as doing it with the Form Wrapper and now we can move down to styling the actual form section. To make this standout, we'll give this a white background color. And also make this a little bit wider and also taller. So we'll set the minimum width be 60 percent of the viewport width with 60 vw. Then also the minimum height of 40 percent of the viewport height. Some padding to add some space in, inside of the form. Let's go forward to Rems and then round off the corners with some border-radius. Good. This is now going somewhere. Next we'll move on to each one of these form inputs. Remember from above when we created any one of these inputs, they were all surrounded in a div section. So we'll first select our form and then we'll target any div which immediately follows. These will have the display type of flex. So if stacked vertically, they'll have the flex direction of column spaces. We can apply some margin on the top and bottom of one Rem and keep the value of margin on the left and right at zero. Next the font size of 1.6 Rems and then moving further inside of this form section, if we go into our divs, we'll then have the input style inside of here. So let's go down to the bottom of our style section. We'll target all of the form section and any input which is inside. First, let's add some margin onto the top and bottom of this inputs, which is going to space these out from the label 0.6 Rems on the top and bottom and zero on the left and right. So I'm padding value again, 0.6 Rems on the top and bottom. Then one Rem on the left and right. The value of one Rem on the left and right, will give us some spacing when we type into the input. This is the spacing we see over on the left-hand side. The borders for our form inputs we'll set them to be one pixel, like gray and a solid line. To match the outside Form Wrapper. We'll also set the border radius to be 0.3 Rems too. This is going to apply to our free form inputs. To also keep this consistent, we can also apply this to our select input too, and we'll do this by adding it for the same section, separated by a comma. Finally, we have the form button at the bottom. So first the background color, which will be RGB, and the red value of one, two, three, the green value of one, nine four, and the blue value of one, two, three. The width, I'm going to set this to be 100 Pixels. Remove the border, add some internal space in with the padding of 0.6 Rems, a matching boarder radius of 0.3. Then finally, we'll also set the font size to be 16 Pixels. Let's also tone down the black color OC for the label, and we can do this just at the bottom and the background color for this. So the text color, let's start with a black color and then we can select a lighter color by hovering over this and choose an enlightened value from the color picker. Once you have a color which you are happy with, give this file a save. Now if we go over to the browser, our styling is now complete. You can also play around with this if you want and change the styling of the layout. That's completely fine too. But one thing to note here is if we try to now click on this toggle button at the very top, our form will now not close. This happens because our modal, or in particular, our modal wrapper, which we have just here, is now taken up or covering the full width and height of the page, which is now covering up the rest of the content. To fix this, next, we will add a closed button inside of the modal and look at how we can do this with custom events.
45. Emit Custom Events: We now have a form but this form is stuck open until we refresh the page since we no longer have access to this toggle button behind this modal. The fix this, we're going to add a little cross in the top corner of our modal which we use to click on to close this down. We're going to do this inside of our AddUpdateForm just inside of this opening form element. So let's add a span, this will have a class of close along with a HTML entity which is the ampersand, a hash 10060, and a semicolon. This is going to give us a red cross which we can now go down and add some styling to. The first thing to do is this form which is the direct wrapper, we're going to set this to make use of the flex box so we can align this over in the top right, down to the form section, we can set the display type to be flex, and then so all of the elements are stacked on top of each other, we can change the flex direction, the column. Since the wrapper is now using the flex box, we can now control this close button with align-self. Let's do this at the very top. This button have the class of close. Align-self will push this individual item right over to the far right if we set the value to be flex-end. So the user knows this is clickable, we can also set the cursor to be a pointer. This now leaves our cross exactly where we want it to be. This cross is now in place but this also now leaves us with something which we need to solve. This cross is placed inside of our AddUpdateForm component, but this is toggled from our parent, App.vue component. At the moment, our app is structured just like this. We have the App.vue, which has our AddUpdateForm, and also the event components nested inside. From this App.vue, we'll have a button which toggles this form to show and hide it as our pop-up modal. This works because showForm is inside of the App.vue too and this is used to show and hide the form component. But what about this cross we just added? This is placed inside of the component, but the showForm dates property we need to change is in the parent, App.vue. We already know how to pass data down from a parent component to a child by using props. What about the other way round? We want to change the showForm data to be false, but this time, from the AddUpdateForm component. To do this, we can emit a custom event from the child up to the parent's component. Back over in the App.vue, knew know how to use a regular event such as a keep or a click event. We uses click to toggle the form using v-if. Alongside these regular elements, we can also create our own custom event. Just like here inside of our AddUpdateForm component, instead of a click, we listen for our own events called close-form. For these event names, it is recommended to use lowercase words separated by a dash. Since this event is passed to the child component, it's inside of this child where we're going to call it. First, we add the list of events to an emits array. This is new in Vue 3, we did not declare them previously in version 2. The [inaudible] is like this especially next to our props, allows us to easily see what data is coming into the component, and then what data is emitted back out to the component. Inside the template, we're arranging our red cross using the span. When we click on it, we trigger our custom event using dollar symbol emit which will set ShowForm to be false. Therefore, hiding our form back inside of our application. This is exactly what we're going to do now. Starting in the App.vue, we'll go up to the template section where we can setup our custom event, pass this down to our AddUpdateForm. As we've just seen, this event is going to be called close-form. When this is triggered, we're going to set showForm to be equal to false. This is being passed down to AddUpdateForm so we're going to head into here, and then we can add this to an emit array. We don't currently have a script section so we can create that now, making sure also to add the export default, and we pass this inside of an array. It should now give us access to our custom event loop inside of the template. The span which we just added for our cross will then run some code when the user clicks on this button, and the code which you want to run is this emit event. We can access this with this dot dollar symbol emit, then pass in the name of our event which is close-form. This dollar symbol prefix means it's one Vue JS's built-in methods of properties, and we will see more of these during the course. Save this file and over to the browser, that's refresh, open up our form, click on the cross, and this will now trigger our custom event in the parent component.
46. Binding Form Inputs: We do have more custom events to emit too, such as adding new entries to our array, editing existing events, and also removing them too. Before we can add and edit our events though, we need a way to capture what the user types in to all of these form fields. A view provides us with a directive for this called the v-model. This means we can setup a data property which is going to hold all the values which the user types in, such as this name. To do this, we'll go to AddUpdate form and then into our script section. We need a data property which is going to hold all of these values which the user types in. First, let's start with the name which we can initially set to an empty string. To see this in action, we can also output this name, loop inside of the template. Now to grab the value which has been added into our name input and store this inside of our data property, we're going to add the directive which we just mentioned, which is v-model and then we assign this to our name and data property. We can now give this a go if we open up the form and type in anything inside here. As we type inside of this field, we can see this value is updated in our template. The good thing about v-model is that it also sets up two-way data binding. This means it not only does the input update our data property but it also work the other way round too. It adds to our name section instead of passing in an empty string. If this had an initial value just like this, we can then save this and over to the browser. Now in our form, this now accepts the initial value which is passed from our data property and this can also be updated too. This idea will soon become more useful when we also use this form to edit the existing events. It can take any current event data and then the user can update using this form. Also when we're using regular HTML, we some some different ways of setting the initial values. For example, if we go to the detail section for our text input, we could set the initial value to be anything which you pass in and we can see this just here. This is a way of setting the initial value with text-based inputs or check boxes, we have the checked attributes and we can use selected for select inputs. However, when using Vue.js, all of these initial values will be ignored and we should instead use v-model for the initial value if we want to provide one. For now though, let's concentrate on adding new events. For this, we need to set up an event object in place of our name. Rather than setting all of these individually, we're going to group these into an event object, this can also be replaced inside of our output. Also our v-model is now going to be the event.name. Let's also copy this and we can add this in each one of our inputs. The input for our details will be event.details, the date, and then also the background too. This is added inside of the select wrapper and this one is event.background. Let's see this now, we should see an object as we type into each one of our fields. The name, the details, the date, and also the background-color will update our event object, which leaves us with all of the information we need to create a new event.
47. Emit Data With A Custom Event: We now captured the form data from the user and stored this inside of this data property. Now, we're going to use this data to add a new event to our array. Currently, this new event data is in the AddUpdateForm component. However, the actual events array is inside this parent, App.vue, which means we need to emit a custom event backup from our form. Let's set this up just like we've looked up previously in the App.vue. Let's go up to the AddUpdateForm. Inside of here, we can add a new attribute which is going to be for our custom events. These custom events, we can give this any name which you want. Mine is going to be add new event. Just about when we added this close-form event, we directly set this showForm data to be equal to false. This is very simple, but our add new event will have a few more steps. So instead of bloating this template, we can instead pass in a method. This method then needs to be settled down in our script section. Let's go through our methods section. At the very top, we can place in our add method. This is going to be responsible for pushing to our events array. Before we go any further, though, let's take a step back and think about exactly what we want to do. The job of this method is to add a new event to our array but how do you access these new event data which you want to push? Well, this can be passed when we trigger the custom event over in our AddUpdateForm. Firstly, just like we've done with close-form, we need to add this to emits array. We're then going to emit this when the Add button is clicked. This is the button which you see just here, and this is going to trigger our custom events into the button. When clicked, this is going to emit our custom events which we pass in as the first parameter, and this was add new events. The difference this time though when we emitted an event called close-form, this didn't have any data which we needed to pass. We can also remove this keyword too, this is not required in our template. But as a second argument, we're going to pass in the actual data which is the events. We could also extract this out and place a method called inside of here if we wanted to, but this is pretty simple for now. Since we're now passing this information alongside our custom event, back over in the App.vue, we can now receive this information using a special variable called dollar symbol event. Just to also be clear here too, this is always called dollar symbol event. We have not just named this event since we pass in some event information. If we had a user which will pass in, this would still be called dollar symbol event. Now, this is passed into our add method, it's now available in our method down below. We'll receive this just like we would normally do with any event, so we can give this any name which we want to. We can test if this is working with a console log, and check we're receiving the event information from our form. To test this out, we need to refresh and go into the Developer Tools, open up the console, and then we can try to add a new event. It doesn't matter which data we're putting for now, this can be any values, click on "Add". What just happened there, we don't see anything inside the console, and now, all forms are being closed. Well, if we take a closer look and take a look at this refresh arrow just on here, we can add the new form event. Watch this arrow disappear if we click on "Add", this now refreshes the page and also clears our console. This happens because the default behavior of the browser is to refresh when a form is submitted. If you've came across this before and worked with other libraries or work with JavaScript, you may have already wrote something like event.preventDefault inside of the method. But Vue does also provide a shortcut for this, and this is to add a event modifier which we can add when using [inaudible] in our form when we click on this button. Back over to AddUpdateForm, here, we're using the V on syntax, and then we can also call dot prevent. This event modifier should now prevent the default behavior. Let's go back to the browser and test this out. Open up the Dev Tools, click on "Add", which then gives us our console log with all of the form data. We can now use this data to push a new entry to our array, but one thing we also need to do is to add a ID. Currently, we have the name, details, dates and backgrounds, but remember that each one of these events also needs a unique ID. If you were saving this entry to a database, we would often get back a unique ID from the database which we could then use. But for simplicity, I'm just going to number these in order, just like we have with our sample data. To do this, we can add a new property to our event object called ID, and then we need to also find out how many events is currently inside of our array, and then add a number onto the end. If you currently have six events, the next one we add will be number seven, number eight, and so on. First, we can grab the length of our current array with this.events.length, and then set this ID to be one extra. Next, we're going to access our events, and then call the JavaScript push method which will push our new events to the end of this array. Then finally, we can close down our form. Let's give this a go. Click on "Add", there's one we just added. Let's try one more. We go for the different color this time and check this is all fine, add this. Now, two events are now displayed in the browser.
48. Emit Multiple Events: We're not restricted to only emit it in one single event from our components. We can emit as many as we want to. Over in the add update form, down in the emit section, we will have access to multiple custom events, and we can use them at the same time too. For example, in the update view, when we add this new event just here, rather than closing the form using this method, we could instead to make use of the closed-form custom event. Then to do this, all we need to do is to go up to our template, and then all we need to do is to add a comma and pass in our second custom event. If we were to use multiple events like this, it may also be cleaner to extract these out of the template and move them to separate methods. As for this, remember we have commented out this dot show form. So now if this closes the form after we submit, would now know this is emitting multiple custom events. Again, the event information does matter, we can add in any data. Click on "Add". The event is now showing, and also our form has been closed, but this time, from our custom events.
49. Modifiers: In our form, we touched on modifiers a little earlier on when we use the prevent keyword, which is going to prevent the browser's default behavior, which is to refresh when submitting a form. There are other modifiers too, which we'll see during the course along with the key modifiers. Key modifiers will allow us to do certain things when a keyboard key is pressed. You could use case would be to maybe allow the user to hit "Enter", to submit the form, rather than always having to use this "Add" button. But this we can copy this click event and then paste it into our Form Wrapper. Copy all of this, click "Section", and then scroll up to the opening form tag, paste this in. Now instead of listening out for a click, we're going to listen out for a key up. This is something which we already know, but the difference is we are going to use a modifier to say exactly which key we want to listen for. Over in the view documentation, have a list of common key aliases which we can use, such as delete, escape, space, but we are going to make use of enter. Of course, this doesn't cover all of the keys which is available on the keyboard, these are just some of the aliases to the most common ones. If we did want to use a key button which is not listed here, we could instead use any valid key names, and we can see a list just here. We can reference any of these keys, such as page down, which we see here. But instead of typing it just like this, we need to make this all lowercase and separate it by dashes. For our example, we're just going to use the "Enter" key, [inaudible] this just after key up. Let's give it a try over to our application and refresh out new events. Now for this to work, we need to interact with all forms since we've added this to the form element. So click on any of these inputs, and from here we can press "Enter", which will then submit our form.
50. Validating Our Form: Let's now move on to add in some form validation to our form. Currently if we just go in and add no values into these inputs, we can click "Add", and this will then submit a empty events. To fix this, we're first going to pass through a validation method which will check if all of the required fields have been filled in. If not, we will then display the error messages to the user. Since there can't be more than one field missing, these are going to be stored inside of an array. So into our add update form, the first step is to add a new data property which is going to store these errors. To pass these errors, we'll then need to add a method which is going to validate all of our form inputs. Just after the data section, that's about methods, and this method name is going to be validate. We will assume that this fills all these rooms each time the user clicks on the add button. First, we want to reset any previous errors. We can do this by setting these.errors to be once again set to an empty array. We'll then check our form. We had some if statements. The first one, we're going to check if this.events.name doesn't exist. We can do this by adding the exclamation mark. Then this if statement will only run if the name property doesn't exist. We could then add the code we want to run in these curly braces or instead, for simplicity, if we keep this all on the same line. This will work fine too. If this name doesn't exist, we want to push a new message to our errors array. We'll first access these.errors and our push method which will then push a new string of text, which will be name is required. Duplicate this and we can now do the same for the event details. We then also have the date in the background which you need to take care of, so duplicate this again. This one is the date. Then finally, the background. Check if this is working by outputting these errors array up in our template. We no longer need this event information output, but we can replace this with our errors array. To actually see this, we need to call our validate method from this button. We'll extract all of this code out of the click handler and then place this into a new method down to the methods at the bottom. First, we'll set up our new method which is you add the event and then paste inside our midsection. Remove the comma from in-between these. Now since this is inside of our JavaScript or our view section, we now need to call emit with this prefix, silver line 1, line 2 and also when we access the event. Now when this form is submitted, the first thing we want to do is to pass through the validate method. Then we'll call add events very soon. Also our add bottom, we'll then call the validate method. Also the same for all the form wrapper. Get results and then call validate. Now if we go down to our validate section, inside here we're going to have two outcomes. The first outcome is going to be the form has errors. We don't want to add to the event. The second outcome is everything is fine. We don't have any errors. Now we want to create this event. If this is the case and we have no errors, all we want to do is to call this add event method. Since this also lives on the view instance, we need to call this with this prefix. However, if there is some errors, we don't want to add a new event. Instead what we'll want to do is to return out to this validate method before we get to this add events section though we can first check if this.errors, which is our array. We can check if the length is greater than zero. This means we do have errors and we're going to return out of this validate method. Let's try this over in the browser. First of all, if we hit the "Add" button, this then calls the validate method and then updates our errors array with our four messages. This means our errors is greater than zero and the return keyword will make sure you break out of this validate method before pushing this new events. That's fine. Again, we can add something in the name and the details, click "Add". You see the date in the background is missing. Let's try this with no errors. Add the date in the background. This now allows us to add our new event. The final stage is to output these errors as a list rather than an array. To do this rather than simply outputting our errors as a date property, would instead going to loop through. Create a div wrapper. We only want to show this section if our errors.length is going to be greater than zero. If it is, we'll output these as unordered to list and then output each one of these errors as a list item. We can do this with v-for and this will be error in errors. Pass in a dynamic key which can be equal to our error since each one of our error messages is unique. Then I'll put the error message. Let's try this out at the new events and the error messages will now outputs as a unordered list.
51. Updating Events: Welcome back. Now to update and remove any of these events, we again need to make use of our custom events. We're also going to make use of this same form which we used to add a new event but this time we're going to pre-fill all of the form details with the details of the current event. The first thing we need to do before we get onto any of this is to enable a click handler onto any one of these events. Once we click on these events, we want this form to then open so the user can then edit this particular event. Now to do this, we're going to listen now for a click over in the app.view inside of this event component or in fact, this is surrounded in the list item, so we can also add this onto the parent wrapper. We'll add a click listener and then we're going to trigger a new method called setForm. The setForm is going to then pass the details of the current event. We're grabbing the event details from our loop. Each time we click on any one of these particular items, it will then grab the event data for the one which we've clicked on. Down to the method section. This method now needs to do two things. It needs to first actually display the form and also pass the selected event to the data property. This data property will then be used as a prop for the form component itself, meaning we have all of the data to pre-fill all of the sections. To begin our method which was setForm, we are receiving the details of the event and a comma at the end. The first thing we want to do is to set the current event data property which we haven't created yet. This is going to be called current event. Actually this is [inaudible] case. We'll set this equal to the event which is passed. If there's an error or no data is passed, we'll just send an empty object. Now we'll set this current event as a data property just above and initially set this to an object. What is going to happen is we'll click on this section or any one of these events. This will then trigger this method, we'll then it pass this event data to current event, which we can then pass to all form in just a second. But first we actually need to display this form. We can do this by toggling this showForm property. We want this to be true, so the form will display on to the browser. Let's check this out. Click on any of these, all form appears, and this all seems to work for each one of our events. This form now needs this event data which we have stored in current event. We can grab this and move up to our template and then pass this to our AddUpdateForm. This is going to be dynamic, so we need the binding. The property name is going to be current event, which is also equal to the data of current event. Overt to the form we can now add in our props array inside of the export default. The single prop which we have at the moment is going to be current event. At the moment we have the current event which is passed to this form. We have the event object which is stored in data. This event object is also making use of v-model to bind this to all of our form input. Now to pass this current event as the initial value, we first need to set this event to be equal to our current event. We can do this as soon as the application loads inside of the mounted hook where we'll set this.event to be equal to this.current event. Let's test this is all working by clicking on any of these events and refresh first. We have the graduation. Let's try the birthday. The conference talk. Also now if we go into our plus icon for add form, the next thing we're going to do is to toggle this button at the bottom, currently we see the add button but if we go into the edit section, we still see the add button too. This is because inside of the template we only have the single add button. We also need to duplicate this and create a update button. This is going to be for update. This also needs to run through the same validate method to make sure that all of the form fields which we use is changing, is still present after they save. We only want to show one of these buttons at a time. The way we can do this is to check if our event has a id property. This is because the id property is only added once we save therefore the event already exists, so we must be updating it. Inside of the first button which is update, we'll add a v-if section, and this is going to check if the currentEvent.id exist. If this statement is true, we will see the update button. If not, we'll add the el-section to show the add button. Let's try this out. We'll go to the add button to begin. We'll see both here because we have not added v-else. We need this because this is not just regular JavaScript. I will now just see the update button, so this is all working fine. Now back over to our two buttons. Now both of these buttons, a first, calling the validate method. When we call this, it needs to know if we are updating an event or going to add new one. To do this, we're going to pass in a string and for this one, this will be update. This one will be add. Now we have a type of event which you'll want to run after the validation. Pass this in to our validate method. This is a variable name which I'm going to call type. This is going to run through all of the validation. Before we go ahead and add a new event, we only want to call this method if the type is equal to add. We'll add an if statement. If outside variable is equal to add, we'll then call this.addEvent. If not, we'll add an el-section, so this must mean that the type is update and therefore we want to call a different method which is going to be called updateEvent. Updating our event is going to look very similar to this addEvent method, so we can duplicate this to begin, it changes to be update. We've not created a custom update event yet, so we're going to just leave this blank. You will also still need to receive the new event details and close the form, so the rest is all fine. Let's now create this custom event over in the app.view up to the AddUpdateForm section. This is going to follow a similar pattern to this add new event section. But this time this is for updating, so we'll add a new custom event called update event. This is going to trigger a update function and we'll also receive the event data. Let's now set this up down in our methods. This will take in the event details which is passed. The first thing we need to do when we are updating an event inside of these event array, we want to find the index position for the one which we want to update. The way we can do this is by calling this.event to grab our full array, and then we need to call a JavaScript array method called findIndex. This going to then call a function, and this function is going to be called for each item inside of our events array. We are effectively looping through each one and then it running a function. Each time we loop through an individual event, we'll then store the value or the element inside of this el property. This is just a variable name, so you can call this anything which you want. We'll then check if this element.id is going to be equal to the id which is passed from this event. This is in a constant called index. This should now give us the index position of our event inside of this events array. If we have selected the first one, this will be index position zero. This will be position one, two, three, and so on. We'll then call this.events and grab the particular one by the index number, and then replace this with our new event object. All that's now left to do is to call this custom event over from our component. Into the add update form, we first need to receive this in the emit array. Then this is ready to call from our method. Into update event and we can replace this with update event, say this, and we can test out our update functionality. Go into any one of these, we can edit the details. Click on update, and this is now saved. Let's try a different one. I'll change the background color to be orange. This all works fine. Let's just do a quick recap because it is quite a lot going on here. We're passing data between two different components. The first thing we did over in the app.view was to listen out for a click on any one of these events. We then triggered a method called setForm. This also received the current event data. Now inside of this method, the first thing which we did was to set a data property called current event. This was equal to our event information and then it also popped up the form. This current event back up to the top was then a passed to our form, which it then used to fill in all of the sections inside of here. It did this because we assigned this to our data property. As soon as this component mounted this update button then called the validate method. Then once you checked through all of the validation, we then called either the add or the update method, which we set up just above using our custom events. Just before we move on, we'll also just check that the validation is working or the update section. If we remove a name, the name is required and also event details is required too. One thing to note though, if we refresh this application, the app will revert back to how it was originally, since we're not using a database to store all of these events in. Next we're going to move on to actually removing these events when the user clicks on this button.
52. Removing Events & Stop Propagation: Just before we move on to removing our events using this Cross button, we're going to fix a small issue which we looked at in the last video. Now, this issue was highlighted. If we go into the Add New section, we'll see we don't have any of these forms pre-filled in. However, if we go into a Edit section, which is all working completely fine. Close this down, and now if we go back into the Add section, all of our work form fields are now pre-filled in. Now the reason this happens is because once you open up the Edit section, this is setting over in the app.view, our data property, which is currentEvent, and this is then sending this to the currentEvent data. Now to fix this, we're going to reset this back to an empty object when we close down this form. When we close down this form, look at the very top, all you're doing is setting the showForm property to be equal to false. We're now going to move this over to a method, and we can also reset our currentEvent data. We'll cut this out, we'll use it in just a moment. This method is going to be called closed form, and then we can scroll down and create this just below. Closed form doesn't need to take in any data, all we need to do is to paste this back in. Now since we're in our view section, we refer to this with this keyword. Then after this, we'll reset our currentEvent data feedback to be an empty object. Let's try this out, refresh the browser. This is empty. This is fine. Then back into our add form, this is now all empty. Now we can move on to removing our events using this Cross button. Removing events will follow a similar pattern of creating a custom event, and colon it from the child components. This time though, it will be called from the event component rather than from the form. Over to the event.view, where we already have this button set up, and very soon we're going to hook this up to a custom event. Look right, it's custom event as always in the app.view. First, let's set up a method which this custom event is going to call. This is going to be called remove. This will also be past event data, so we know which events we want to remove. Now, just like in the update section, we also need to find the index position, so we know which one we want to remove from our array. We can copy this and paste this inside of here, and we can only use this index position to select the correct event. First we'll select this.events, then the JavaScript array method, which is called splice, is going to allow us to remove a particular item. This is going to take in two things. The first one is going to be the position of the item which we want to remove. We know we already have this from the index. Second of all, the number of items from this index position. We only want to remove the single event, meaning the value is one. Then we scroll up to our templates and the events section where we want to pass our custom event. As an attribute, we'll pass in our crystal method, which is remove-event. This is going to be equal to our remove method, which will also receive the event data. The next step should be pretty familiar. We go over to the event components, and then we're going to add our emits array. This emits array is currently only going to take in one event, and this is remove event. Up to our button. To trigger this, we need to listen out for a click. Then we'll click on this button, this will then call the remove method, which will now set up just below. We don't have a method section, so we need to set this up, and then set up our remove method where we're going to emit our custom event. Since we're in the view section, we need to call it with this keyword. Enter our event name which is remove-events. Then we want to pass the event data. We already have this saved as a prop, so we pass this in as the second argument. Let's give this a try. Over to the browser and refresh. Now if we try and clicking on any of these events, we can see in the background that the event has been removed. We'll also see this form popping up. Let's try this once more. We'll remove the birthday, and we can see from the background that this is gone. But why are we seeing this form? Well, this is because even though in this component, we are clicking on this button. This button is nested inside of the app.view and inside of this pairing app.view, we also have this surrounded in a click listener, which is triggering setForm. Then inside of showForm if we scroll down, we can see that we set in this.showForm to be equal to true. Why is being called when we click on the Remove button? Well, the reason this happens is because of something called event propagation. This is nothing to do with Vue.js, just something which you should generally be aware of when using JavaScript. Yes, we are clicking on the remove button which is nested inside, but clicking on an event inside of the HTML would trigger a chain reaction. To see this, we can open up the developer tools. Right-click and inspect. What we need to find is this Remove button, so into the body section, the app section. Then inside of here we have an unordered list with all of the events, and then we can select one of our list items. If we open this up, this is in a article wrapper, and the button is nested in the countdown section just here. As we can see, this puts in is deeply nested, it has surrounding divs, it has list items. In order list the body section, and it goes right through to the top of the tree, which is the window. Even though we're clicking on this button, it will trigger a series of phases. It will start at the very top and this then will pass down through all of the elements until it reaches our button. This is called the capture phase. Then it reaches the button or the elements which we clicked on, and this is called the target. Therefore, the stage is called the target stage. Finally, once it reaches this button, it then climbs back up through all of the parent elements one by one. In doing so, we'll also trigger any events on the way back up. As we just looked at, one of these events on the way back up will still open the form. This stage is called the bubbling phase. Now, you don't need to fully understand all of this for now. All you need to understand is that when we click on an event, it goes through all of the parents DOM elements and we'll trigger others along the way too. All of this is referred to as events propagation. Vue.js also provides us with an event modifier to stop this behavior if needed. Over in the event.view. Just like earlier when we added the prevent modifier which stopped the default behavior of the browser, this stop modifier will stop this event propagation. Let's save this and over to our project. Click on these. Now these events are successfully removed. Just to finish things off, I'm going to do a small refactor over in the app.view. At the moment, inside of this remove method and also the update, we are duplicating this line of code. To prevent this, let's place this line of code into its own re-usable method. We cut this out, and also this one too, and then create a new method called findEventIndex, where we can paste in this line of code. This method also needs access to our event, so we can crop the event ID. Or alternatively we can simplify this and just pass the ID of the events. We can do this one called findEventIndex, so let's grab this and first into the remove method. We now don't have access to this index constant, so we're going to replace this with a method call. We can call findEventIndex, where we also need to pass it in a valid ID which we can grab from the event.ID. This also needs to be called when the this keyword, and we can also copy this section and replace the update section where we call the index. We'll paste this in. Now we need to return the value from this method. Instead of storing this inside of a constant, we're going to return its index number, which is going to be then available inside of these two sections. Let's test this out. First of all, we can remove an event, and this works fine. Then the update functionality, you change any of the details. Click on Update, and this also works fine too.
53. Section Intro- Validation & A Deeper Look At Props: We don't just want to throw any old data into old components. We want to be more specific about the type of data we're going to receive and the formats which it's in. Otherwise, we may get errors and we don't want those in our application. We're going to take a look at this in the next video, and then we'll follow up with taking a look at something called non-prop attributes, which is a different way of passing data between our components.
54. Prop Validation: In our project, props are an important part of how it all works. It is a good idea to add some validation to make sure we get the correct information passed through our components. Inside of this main app.view file, this is the top-level component, so this is not passed any props. To begin with the prop validation, we're going to go into the events.view components. The first thing we need to do to add validation is to change this array into an object, that's common results, and then we are going to reinstate our props property for this time as an object. The first one we have just above is the events. Its most simple proof validation, it cannot just declare the type of data which we expect to receive. The event is going to be an object. Then separated by a comma, we have the daysLeft. The daysLeft is going to be a number. Then finally we have showPastEvents. The data type for this is a Boolean. This is just a simple true or false, so we can hide the events if the days has expired. We can also pass in a valid datatypes too, such as array, function, symbol, date, and string. Let's try this out. Over in the browser, refresh, we see everything is working perfectly fine. Open up the developer tools and we can check there's no areas inside of the console. Yes, that's all okay. But now if we go over to the app.view, we'll make a change to the data which we are passing it down to this event component. For example, let's go for showPastEvents which is either true or false. Now, if we remove the binding, this will not mean that this will be passed down as a string value rather than a Boolean. Open this, refresh, and now we'll see a warning for all of our six different events. The warning is the invalid prop type, the type check failed for the showPastEvents prop. We see we expected a Boolean because this is what we set inside of our components. But then we got a string. We're not restricted to just one single datatype either. If we did want to accept a string alongside this Boolean, all we'll need to do is to pass this in as an array. It's around this inside this square brackets, then separated by a comma will also accept a string value. Refresh, and now our errors will now disappear. We don't want to Boolean for this particular example, so we can remove the square brackets and also at the binding back into here. These errors which you see they're only shown in developments and can be really useful if we make a mistake or change our data unexpectedly. We can't go even further too by not only declaring the datatype, which we currently doing, but to also restrict the values which you receive too, to do this we can pass in extra properties and turn this into an object, so call this us out and add the curly brace is to turn this into an object, we can then pass in the type with the type property. What we're going to set this prop again to be an object, the daysLeft, we do the same for this and the type property, and also the same for all Boolean. Now these are all objects, these can now accept multiple properties. For example, if we wanted to make sure this prop is required, we can set this to be true. Also the same for the daysLeft. This means that this should flow in an error if even one of these properties is not passed down to these components. Test this out over to the app.view, we can remove any one of these. Let's go for the daysLeft. Lets call this out temporarily. Now over in the browser we see the missing or required prop of daysLeft for all six of our events. Over in the event.view, notice here how we have left off the required property, to showPastEvents. This is because this prop type is a Boolean. If we were to not pass down this prop, a Boolean value will always default to false anyway, meaning it would still appears to be present anyway. This is just something to watch out for when passing Boolean props. Let's just quickly reinstates its daysLeft and save this file. Props can also receive a default value too, which we can use if no value is provided. Now instead of saying that this prop type must be present, we can also say if this is not present, we're going to instead pass in a default value. This is the number type, and let's go for a number of 10. This currently won't make a difference if we refresh the browser because we are still passing the prop down to these components. But again, if we could this out, refresh, although events will now default back to 10 days left. Let's just undo this and save this file. This just work for things such as numbers or strings, Boolean when validating the functions, arrays are objects. We need to pass this default property as a function. Our event is the type of object, so we can give this a try, remove the required property, and then it changes to be the default. For this since this is a object, we need to set this up as a function. Then if no event prop is passed we're going to return this default object. This needs to have the same setup as we currently have inside here. We have the ID, to each event, that just give some random information. We have the name, which is a string value, the event details. If is fine, save this, and of course it shouldn't affect anything which we currently have because we are still receiving the event prop. But if we do remove this, we should now kick in our default events with data which we pass inside here. What we actually want to look at is the validator property. Let's just add our event back inside here, and then back over to our events. The validator property is a custom function which gives us a much more flexible way of validating our props. It's laid out in a similar way to our default function but instead we replace this with validator. This function will also take in a value, which is the value of the prop which is passed. Currently this will be the event. We can now validate this value in any way we want using JavaScripts. For example, if we only wanted to allow a certain event names, we could do something like this. Let set up open array with the aloud event names which you want to pass through this validator. Let's go for two of our current event name. We have birthday, and we also have a Christmas too. Then we can use the JavaScript includes method to check if any of our events includes either the title of birthday or Christmas. We know the event information is passed inside of this value, so we can access our objects and then the event name. If any one of our events include the title of birthday or Christmas, we then want to return this value, and this will be either true or false. Let's save this and go back over to the browser into the developer tools. We'll now see this validator has failed for our four events which don't include the title or the name of birthday or Christmas. We don't need this validator function inside of this project, but it is useful to know this is there and it gives us a really flexible way of validating our props. We can also add prop type validation to our AddUpdateForm too, scroll down to our scripts action. We only have the single prop inside here, so we can comment this out, set this app has an object, base in our current events, which has the type of object. Save this file. Now if we refresh the browser, open up the console, everything now still works as expected.
55. Non Prop Attributes: In the previous project, we added a dark mode option, and I'm now going to do something similar which is to apply a grayMode. If this color scheme which you currently have is a bit too much for the user, this should be pretty familiar, we reduced using many of the techniques which we've already used. Starting over in the App.vue, we first need some data properties. Inside of the returns section, the first one is going to be grayModeSet, this is a Boolean value which will be currently set to false. Then also a grayMode object which is going to contain the color scheme, but our background and also our font color. This will be for our CSS silicon, first set the background property to be light slate gray. Then also our color property for the text, and you can choose any color which you want and I'm going to go for a hex value of 454444. Move to the templates, this also needs a button so we can toggle this on and off. I'm going to place this inside of our options div, and also in-between these two color buttons. The first step is to, at least now for a click on this button and then we're going to toggle this grayModeSet to be on and off. Just like we did above with the show past events, we're going to do the same, but this time for grayModeSet. We'll set this to be the opposite. We could also add some text inside of here, but instead I'm going to use a hide my entity, which is going to be the sun or the moon. To make this dynamic, we need the double curly braces and we'll also make use of the JavaScript ternary operator. If gray mode set is currently true, we're going to set the icon which is the moon. So the moon icon is the ampersand, the harsh on the number of 9788;. If this is false, we show the picture of the moon. We set this default value to be false, so we do see the icon of the moon inside of here. Let's click on this, we see the sun. So this all seems to work in fine. Now the styles which was setup as a data property, now needs to be passed down to this event components. We can change the background color and also the text for each one of these events. In the past, we've passed down props to this event components. Just like we see here. We could once again do that, but this time we're going to take advantage of non-prop attributes. This works in a similar way to the props which we've looked at in the past. Well, instead of inside the components, the template will automatically inherit these values. For this particular components, the article is the main wrapper elements, and any non-profit attributes to pass down will be automatically inherited by this main wrapper. They also appear just like a regular attribute inside of the opening tag, so if we pass an ID attribute, it would look something like this. Let's remove this and to do this, we're going to go back over to the parents App.vue, and then we can pass this down at the event components. Using the same example, we'll set the ID equal to a string, say this and over to the browser, we can refresh and open the developer tools. Use the selector and choose any one of these events. These are stored inside of an unordered list, and if we go inside of our list items on the article, this around an article has now inherited our ID. This means if we go over to our Event.vue, we no longer need to register these as our props, they're automatically inherited inside of the template. Now as a more practical example, we're going to replace this ID with the styles which you set as a data property. We combine this as a style attribute where we're going to pass down our backgrounds and also our color. Inside the data section, we have this set inside of our grayMode object. Now we only want to pass down these values if grayModeSet is true. We can now use the JavaScript ternary operator to determine if we're going to pass this as an object and we can do this using the JavaScript ternary operator. We can first check if grayMode is set. If it is, we then go to pass down our object, which is grayMode. If not, we'll just simply pass down an empty string, so none of these styles will apply. Let's give it a go over to the browser, and our toggle is all now working. Inside the developer tools, we can see this if we go into the elements section, your unordered list, and then it down to our article where we now have this dynamic style property, which has the background color and also the text color too. Click on the Sun and then our regular CSS would just apply. Now with all this work and we're just going to apply some styling it to these three buttons at the top, and the show past events button alongside this toggle is going to remain over on the left. We're going to push this addNew vamp button over to the right. Do this in the App.vue, up to the three buttons, and we're going to group our first two buttons together inside of a div. So we can grab these, add a div, and place these back inside. The style, and we're going to give this a class of option on the skull buttons. This now means our options class now has this div and also this button. We can make use of the CSS flexbox to set the alignment and add some spacing in between these two sections. Downscale style section. First of all, these options wrapper, the display type of flex, which will now maybe we couldn't set the justified content to give us some space between. This now gives us the spacing in between, so we can now focus on this time and for these individual buttons, let's begin with this addNew button. If we go back up to our template, the button over on the right, as this class of addNew. Then our two buttons on the left have this class of option buttons. Down to our style section, let's first go for the addNew button. We can make this a little larger with the font size of three rems and also the font color, which will be an RGB values. So the red is 92, the green is 84, and then 84 for the blue too. The cursor, this will be a pointer, so it changes when the user hovers over this button. Then also remove the default background and also the border too. Now, the options group on the left, which had the class of option buttons. Then when we want to target the two buttons which are nested inside, begin at some padding of 0.5 rems on the top and bottom and then at one rem on the left and right. This will just make these buttons a little bit larger and to also add some spacing in between, we can apply some margin over on the right, the background of none. Then let's go for the border. First of all, the border-radius of one rem gives us the rounded corners, and then the actual border, which is going to be two pixels wide, a color of light steel blue, and also a solid line. The font size of 1.2 rems and then finally the cursor, just so much has gone to be a pointer. The final touches to also make sure that we have vertical alignment from our two buttons on the left to all button over on the right. We can see that this puts it is little bit lower and the way to fix this is inside of the wrapper, and all we need to do inside of here is to set the align items property to be in the center, which now lines up our two sections. There's also a few more things to discover with non-prop attributes, and we're going to discover these next.
56. More On Non Prop Attributes: We now know how to pass basic non-prop attributes, but there's also a few more things we can look at too. One of these things is to cancel the auto inheritance. We know from the last video that the child component inherits the attributes automatically. But if we don't want this to happen, we have full control over this. Inside of the child component, which is the Event.vue, we go down to our script section and we can add a new property, which is inheritAttrs. If we then set this to false, this will now mean our template will not automatically inherit all of the non-prop attributes which we pass down. We can prove this is working by toggling, let's put it just here, and our color scheme stays exactly the same. But why would we want to do this? Well, let's say, for example, we don't want all of these attributes to be inherited at this top level wrapper. Well, we're not restricted to just use this top level wrapper, Vue gives us access to these attributes anywhere inside of this template, and also in our script section. Let's do this by adding the mounted hook, and as soon as this component is mounted, once it logs to the console, the value of this.$attrs. So if we access in our attributes inside of our script section, we use the 'this' keyword. If we're directly accessing them in our template, remember, we don't need to use a 'this' keyword, and we can access it directly over to the console. Let's check this out. Refresh. We now have six different attributes. Because we have six different events, inside of here we, still have access to our style object and we can access any other non-prop attributes which are passed down to this event. This dollar symbol attribute is a built-in view property. We're now free to use this anywhere we want. For example, if we wanted to place this in a different div, we can do so by using v-bind. So remember, we are currently not automatically inheriting this, so this article doesn't have access automatically. Now, we can place this into any other section using v-bind, and then pass in our attributes into the console. At the top of our app is the options object with all three buttons. So blow this, open up the unordered list. For each event we have the list item, we have the article wrapper, and so our article. Then the div with the class of data, let's toggle this button. Then we see our style attribute is now been added to this section with this. This is just an example of how we can place these attributes into any section for our template. Something we also need to be aware of is if we have multiple root level elements. To see this, let remove our v-bind. If we also, comment out our inherited attributes. Now, if we go back up to our template and if we were to cut out this remove button, the full div section, and then places just above our article. Remember from before that this article was inheriting automatically. All of the non-prop attributes which are passed down, but now we have two elements alongside each other. Which one would automatically inherit these attributes? Well, let's take a look in the browser. Back into the developer tools, into the console. Refresh. We now also see some error messages in the console since the inheritance will only occur if there is one root element. So it does makes sense that this behavior does exist, because if we do have two elements alongside each other like this, Vue would have to guess which one to mount to. We will then resolve this by again using our v-bind syntax and then tell Vue.js which one of these elements we want to bind to, just like before we bind our attributes. Then if we go back over to the developer tools, into the console, we no longer see any errors. The final thing we're going to look at is also a passing event listeners as attributes too. So far, if we go to our App.vue, and then where we render out our events components. Up to now we've only looked at passing data such as styles, but passing an event listener works just the same. If we wanted to listen out for an event, such as a click, we can also pass this into our components at @click. Then just like with any other event, we can then trigger a method, or we can just do some simple JavaScript, such as setting a gray mode to be equal to true, save this over to our browser. Remember in the Event.vue, we are currently binding these attributes to this remove button. Now, if we click on this remove button section, we see the darker background color has appeared, is we are setting it gray mode to be true. We can also now see this if we go over to the developer tools, where we're still doing the console log for each one of our attributes. If we open this up, and then it's the target section, you see we have this style property, but also now an onClick event handler. Now, this is just a simple example of how a click listener works with non-proper attributes. So we're going to remove this as we don't need this in our project. Also, over in the event, if we toggle this, we're still passing the attributes to the button. We want these to actually apply to the article so we're going to remove all v-bind and then pass this over to our article. Let's test this out by clicking on our toggle, and everything is working once again. This is how non-prop attributes work. Next, we'll take a look at some other ways of passing around data using slots.
57. Section Intro-Slots & Dynamic Components: So far, we've looked at various different ways of passing data between our components. We've looked at how we can pass props from a parent down to a child component, and how we can omit custom events backup. But next in this section, we're going to take a look at something called slots, which is a really flexible way of passing data between our components. But also, we'll have a new project to style which is a dashboard application. Next, we'll take a look at how we can download this and get started.
58. What We Will Be Building & Starter Project: In the upcoming sections, we are going to be taking a look at some different ways of parsing around data between our components. The section will be focused on slots. Then in the following section, we'll take a look at something called Provide and Inject. To do this, we have a fresh project to begin working with. This is a dashboard which you can see just here. We'll be building this throughout this section. To begin, we have a overview section, which we're going to click on from this left menu. This will then give us access to our data which is going to be separated between our three components. We'll have the item sold where we'll parse down the data, the sales total, and also, we'll filter by the top selling item too. The next link is for our orders. This is going to loop through all of our orders and display them on this page along with our total sales components. This will be re-used from our overview section. As well as, this will also have our bestsellers page. This will again loop through all of our orders. We'll then extract all of the sold products and then we'll total them up in order of the quantity sold. This project will allow us to get a deeper understanding of how slots work in Vue. As with the previous projects, if you go to the GitHub repository. Inside of the downloads, you have a dashboard's data. This is just like the previous projects where we have a minimal Vue CLI setup. Pretty much the only difference is in the source, we've removed the Hello World examples. Also, we have this orders. js file. This is an array of objects and this is just some sample data, some sample orders to get us started working with our data. All it contains is a ord number or an order ID. Then, we have a array of items which have been sold for this order. To get started, once you have downloaded the starter, drag this over into your text editor. Then, we can run NPM install. Go ahead into the project directory and then install the modules with NPM install. Then, a server will run NPM run serve to start our development server. Then, open this up in the browser. Next, we'll set up the components and pages which we're going to need for this project.pa
59. Project Pages & Components: This section is going to be focused on something called slots and also dynamic components. To work with both of these, we first need some content to work with. Remember, earlier, we said that components can be placed anywhere inside of our source folder. We don't just have to restrict them to a components folder. With this in mind, I'm going to create a new folder, again inside of this source directory, and this is going to be called views. This also contains components or.vue files, but it is common to have a views or a pages folder to better organize our components which we are going to be using for our main pages. So just like we're seeing when we had a look at the final version, the pages which we have inside of our application was the overview. We had the sales page and also the bestsellers too. This views folder generally contains these types of files which we're going to use to switch between four different URLs. Considering this file structure we have in the source folder and then inside a components on a views folder. Both contain the.vue single file components. This approach means we can organize them so the pages which we're going to be switching between are all grouped together. Here the three pages we have for home, contact, and account are also links inside of our header. Meaning if we click on the account, for example, the account.vue file will then be displayed. These page components do not have to contain all of the pages contents. They too can also be broken down and contain multiple components. This example next to user components onto the page and this component can also be reused on as many pages as required. Over to our application where we can start to set up our pages and our reusable components. So first of all, in the components folder, create a new file and this is going to be for our BestSeller.vue. Also alongside this, we'll also have the ItemsSold. So remember, these are just the single components which we're going to place into our pages. After this, the third one is going to be for the SalesTotal, and finally, the last component which we need is for our Sidebar. Okay, close this. Now, inside of our vues, we're going to create our free pages, which we've seen before. So the first one will be for the BestSellers, and these pages or these vues will also have the.vue extension, the Orders, and then also our Overview page. So the.vue files inside of our vues folder and also the ones in our components folder have no difference at all other than the fact that they're organized into different folders. So let's begin by adding some content into our vues. Starting with the BestSellers, and all we're going to do inside here is to add our template and all this is going to contain for now is a level 3 heading, a class file styling of page-title, and this one is going to be Best Sellers. Select all of this and copy into our Orders, place this in, paste the title, and then the same for the Overview. Then our components. Just to be clear, the BestSeller component is going to be for the number 1 best-selling products and the BestSellers page is going to be a list of all of the best-seller items from the highest to the lowest. So the template, again a HTML article tag, and this will have a class of stats. We're going to make use of this in the Overview page, so we'll have this as a component in the left and then we have the ItemsSold and then the SalesTotal too. Now, let's give this a level 4 heading of top selling item. Select all and copy. Down to ItemsSold, we can paste this in. All you need to do is to change this level 4 heading, the ItemsSold. Next, the SalesTotal, which will be very similar too which will have the title of total sales, and for now, we're going to leave this signed by empty. Okay, good. Now, we have some content in place. We have our views, all the pages, and also some components. In the next video, we'll take a look at how we can pass data down to these components using slots.
60. Introduction To Slots: Previous videos have focused on passing data between components, using props and emitting custom events. This upcoming videos are going to show you how slots work in Vue.js, which is a really flexible way of passing around data in our apps. This example we looked at in the last video included a page component, which was the account.view. We reuse the components nested inside, we pass information into these components using props, just like we see here for the user's name. Another way of outputting our components is to use an opening and closing tag in place of the self-closing style from before. This style also works exactly the same too. For this style also now leaves us with the option to add things between our components tags. It would pass in a level 3 heading, but what exactly happens with this heading between our tags? Well, this is completely up to us. If we do nothing, the content is ignored or we can use slots to use this data in our user components. Looking at our user components template on the right, we have another element which contains a free list items, and this is all pretty standard. Take a look above this nav, and we have what is called a slot. Slot acts like an holder for the content we pass into it between the components opening and closing tags, and in our case it was a level 3 heading. This slot element is very flexible and it can be placed anywhere inside of our component's template. This approach may help you think in why do we even have slots available when we can simply pass down props? Well, this is a very valid question and one which is very understandable, looking at a simple example like this. But it is also a lot more to slots and a lot more use cases, and we'll take a look at these in the upcoming videos.
61. Overview Page Structure: You see these slots in action inside of our project. We first need some content to work with. Currently, we have all of our views and our components setup, but we don't have anything registered on the page just yet. To do this, we're going to go over to our App.vue. Inside here we have this div with the class of app_wrapper. This class just simply points to some styles which is going to set the font family and also the base font color. What we're going to do is to add some structure to this app, and also import and register our first page which is the overview.view. To do this, we'll import this just like any regular components. The name is Overview and this is from./. This is in our Views folder, and then component name is Overview. Setup our components section. Inside here we only have one component for now, so pass it in. We see an error because we've not used this just yet, but we'll first add our structure inside of our app_wrapper. The first thing we're going to do is to create a header section, and this is going to contain a level one title with the text of Chris's Dashboard. This header section a div with the class of content, and this section is going to contain two nested sections. The first one is the sidebar with links to our free pages, and then on the right-hand side we'll have some content, which is for each one of these pages which we've clicked on. First of all, the aside element. Then after this, the main. The aside section for now is just going to contain three different links. We don't need href because we're going to control it using view. The first link is Overview. I have two more pages, the second one is Orders, and the third one is Best Sellers. That's it for the aside bar. Then into the main section what we're going to do is, first of all, pass in inside of these small tags, and inside of here we're going to pass in some breadcrumbs to let the user know exactly where they are on our site. This would say, something just like dashboard/overview. We'll change this section depending on which page we are currently on. To style this I'm also going to cut out the dashboard section and add a span element. So we'll give this a different color inside of our CSS. After this, the actual page content, and for now we only have one component to display. This is the overview, we will post some data to this overview very soon. But for now, let's go down to the style sheets. We're going to begin with our header section. The header section is going to have a white background color. It's also going to make use of the css flexbox too. Firstly, background which is a white color. Then add a border on the top and also the bottom. This will be one pixel, a solid line, and then the color of ddd. This Chris's gray line on the very top, we'll also add one on the bottom, and then soon we'll also add a vertical line which is going to separate our sidebar only page content. Duplicate this. I will also add one on the bottom. After our header section, we have our content section, which is the div surrounding both the aside and also the main section. This is basically all the content below our header. To style this and position this, we'll set the display time to be flex. The default flex direction is raw when using flexbox. Now these two sections will now appear side-by-side. To set the width of each, one, we're going to use the flexbox fonts again, and the aside section can have the flex value of 1, and then the main section will have the flex value of 3. This means, the main section will take up three times the width of the sidebar, over the aside bar the flex value of 1. But also a much in boarder, but this time this is going to be vertical. Then we need to place this border onto the right, and that's our line just there, and we need to make sure that this line is the full height of the page. What we're going to do for this section is set the minimum height be 100 percent of the viewport. This line now goes right down to the very bottom. Now over to our main section of on the right, it has the main wrapper. The flex value of 3, so this takes about three times the available space from the sidebar, and then a padding value of 1ram. Last thing to do in this section is also to change the color of our breadcrumbs. So we're now going surrounded the dashboard with this class of blue. We can now use this to set the color. The text color will have a RGB value of 25, 149 and 243. We can also space out inside of this folder /2 of HTML all we need to do is to add a space just before and after and there we go. This now gives us a base to begin to explore our slots in the upcoming video.
62. Slots In Practice: In the slides a little earlier, we discovered we can pass data to a child component by passing it in between the components, opening and closing tags. This method is called slots, and we're now going to setup some slot content. The page we currently have set inside of here is this overview which we registered in the App.vue. This page will now display our three components of bestseller, the item sold, and also the total sales too. Just like here in the final version. Although this, we first need to register these inside of our overview.vue file. We will head into here and to do this we will create our script section, then register our three components. The first import is going to be for items sold. This is going to be from the location which is../components, and then into the items sold components, which changes. Also, we can replace this with the @ symbol, and this is a shortcut to the source folder. We need to duplicate this two more times. The second one is for the sales total, change the file path. The third one is the bestseller. Down to export. The first thing is to create a name for this component, and mine is going to be the overview. Then register our three components so we can use them inside of our template. We have items sold, the sales total, and also the bestseller. To display these, we'll create a new section just below our title. This section is then going to display our three components and then we'll nest in-between our slot content. The first one is items sold and also closes off. The second one is for the sales total, and then the bestseller. Save this and we see the titles for all three of our components, which will start inside of our level 4 heading. Now, we're going to pass our slot contents in between each one of these elements. Just like we see on the final version, we have the items sold, the sales total, and also the top selling item, but now this is just going to be some static data. But very soon, we're going to grab all of these values which is contained in our orders.js file. Back to our overview and in between the items sold elements, we're going to pass our slot data. This can be anything such as regular text. We can pass it in HTML elements and also view js data between double curly braces too. I'm just going to output the span, and the span is also going to have a class. So we can apply some styling, often light-text, place in any value inside here and copy this. The next one is for our sales total, place in any currency symbol and also any money value too. Finally, the bestseller. Any product for now is fine, I'm going to place in the Vue hoodie in size medium. Save this, and at the moment we don't see anything inside the browser. This is because we first need to tell Vue.js exactly where we want this content to appear in our components. This is pretty easy to do. All we need to do is to go over to our components and then add in the slot elements. This is basically an outlook for the content which we pass through this component. We can go anywhere inside here. But I'm going to place this below the level 4 heading, the same for the sales total, as in our slot. We see the values that appear in it below each one of our titles, a bestseller. Other slots in here too, and this now appears on the page. Good. Now let's add a little style in over in the overview page. In the beginning, remember, all three of these sections were surrounded inside of this section wrapper. Let's make these go across the page, just like we see here, we will target this section and set the display type to be flex. To do this we'll need a style section. This can also be scoped, so only applies to this component. [inaudible] section and set the display to be flex. Some spacing, we can also take advantage of justified content and distribute all of the available space to be between these three elements. Also place a border around all three of these sections, add a target, these individual headings and slot contents. We are now going to go over to the App.vue. This style section is not scoped so all of these styles will apply to any components, including all three of these sections. Begin our level 4 heading, which is the title for all three of our components. We will remove any default margin by setting this to be zero, and then add some padding to the bottom of this title to give us more spacing from our slot content. This slot contents also had a class. If we go to the overview page and up to the templates, this had the class of light-text. Let's grab this. All we're going to do in this section is to reduce the font-weight to be a value of 300. To apply the border around each one of these items, if we go into the component, each one of these articles have the class of stats. Inside here, we'll set the border to be the weight of one pixel, a solid line, and a light gray color, which is ddd. The border-radius of five pixels. Then some internal space in with the padding value, which will be one 1rem. Good. Let's have our components now styled. Just to work with this header section, I'm going to reduce down the level 1 heading. This is a little bit too big, so we'll tag the header H1, and then reduce the font size. Let's try 1.4 rems, and that looks better. I can also play around with the font-family too, I'm going to remove this and change it to be Avenir, but you can also change it to suit your style. This is how we can use slots at a very basic level. In the next video, we're going to take a look at the scope of slots, and also how to pass dynamic data.
63. Passing Dynamic Data & Scope: In the last video, we looked at passing data as slot in the form of a HTML element. This can be anything we want, including a plain text and also dynamic content too. These three sections we have here of items sold, sales total, and bestseller will all include dynamic content as props from the parent app.vue. Now, at the app.vue, we now go on to set up our free props into the overview component. The first prop is going to be quantity of items sold, which is equal to 32. This, again, can be any value. It doesn't matter just yet. When dealing with numbers, too, we also need to use v-bind. This is evaluated as a JavaScript expression rather than a string value. Next, the total sales value. Add anything inside here. The third one is going to be for the bestseller. This doesn't need to use v-bind since this is just going to be a string value. Since these are props, we also need to go over to our overview component and register these in our script. We can also add some simple validation. So we're going to set this up as an object. The first one is total sales value, and the type is number. The second one is quantity of items sold. This will also have the type of number too. The third one is for the bestseller. This one has the type of string. These three props can now replace the content which we passed into our slot. The first one, remove the first value of seven, and we can replace this inside the double curly braces with our prop, which was quantity of items sold. The next one, we can still leave in the currency symbol, but replace the value with the prop, which was total sales value. The third one, for our bestseller, and this is missing, so let's take a look. This just wants to be a lowercase b, and there we go. This all works now with our dynamic data. As you can see, slots don't care if the data is text, HTML element, or even dynamic content just like this. But when passing dynamic data, just like we are here, which components have access to this data? Inside of our overview, for example, we have this quantity of items sold prop. This is inside of our overview component, but it's also then rendered inside of our items sold component too. Which of these two components has access to it? Well, you may have already guessed that since we are currently using this inside of our overview file, that this file has full access to this data. This is correct. But what about the child component, which is items sold, where this data is being rendered inside of? Well, let's grab this and take a look into the item sold component. Then we can try to output this inside of the double curly braces. We don't see any data now displayed, so this doesn't work. But how about this being available as a prop? Well, we could try this too. We could place in the script. We can see we have access to this via props. This one, we still don't have access to this data. This now means that the actual child component, which is displaying the content, is only responsible for presenting this inside the browser. It doesn't have access to our variables. These are only available in the scope of the parent. If we did want access to this data inside of both of these components, there is a way we can do this, and this is by what is called a scoped slot. We'll take a look at these very soon.
64. Fallback Content: Here here, we have this slot outlive the content which were passed down, but what if we add something in between these two tags too? Well, we do this if we want to provide some fallback content to show if nothing is passed to the slot. We can maybe add a message which has no data available. Over to the browser, we don't see this message just yet, but if we go over to the parents, which is the overview, and then it temporarily remove the span elements which we pass in at to item sold. This is now replaced with this message of no data available. This is used for any fallback content if we don't provide any slot data to our child components, I want to undo this and save this file, but this is useful for a number of situations. For example, we may not always have some slot data to pass down to the child components, especially if we are reusing the components multiple times or there may be a delay in generating the slot data, so if this data which we placed inside a he was maybe fetch from a database or an API. While we're waiting on this data coming back, we may want to display some loading spinner or a placeholder message. Also, if we are using a component, let's say five times over in our project, if the data is going to be the same for four of them, we could make use of the fallback content to provide the default for these four components. Then it changed the fifth components by passing slot data. This is really simple but really useful too. Next, we'll keep on subjective slots and discover how to place multiple slots in our components.
65. Named Slots: Here in our overview page over on the left, inside of each one of these three components, currently we are only passing one single element. This is completely fine, but for more complex situations, passing a lot more content into one single slot may not be ideal. For this use case, we have what is called name slot. This is a way to distribute this slot content into different locations in the child components. We could have multiple lines of codes and then place them over in our components into different locations. The first step to do this is to basically group together our content inside of the template element. For this, we're going to create our first template. This is going to pass down an icon to all three of these child components. We'll have our first template and we'll also add in a second group inside the template 2. What we're going to do here inside of the second template, is to grab our original span elements and move this up into the second section. The first section we're going to add in our icon, and this will also be a span element. The HTML entity for this icon is going to be the ampersand, the hash and then 128200 followed by this semicolon. Since these are going to be name slots, we need to give each one of these templates a unique name. We can do this with v slot. V slot is also a directive. I'm going to call the first one the icon. Then down to the second template, again adding v slot. This one is going to be for the quantity. So I'll give this a save, and we're in-between the items sold sections, so if we'll go over to these components, we now need to provide our two locations where we want these to display. To begin with, the the icon is going to a place alongside our level 4 head in. Adding our slot, and then figure which content we want to be placed inside here, we add the name attributes. This will be for the icon section and the second one was for the quantity. Close the browser and we see our icon and also our quantity too. If we wanted too, we could also omit one of these slot names, and use this as the default. This is only leaving up one of our slots with a name, and this is going to be for the default content. The way we set this is to go back to our template, set this as the default location and it still works exactly the same too. Let's now go down and do the same for our sales total and the bestseller too. These need a template and we can bring up this total sales value and then place our icon inside of the first section. This will also be a span, if only if this one is the ampersand, the harsh 128176. Add to our template, adding the v slot directive and this one is for the icon 2. The second one placed in the v slot directive, and this one can remain as the default location down to the bestseller and this would be exactly the same. The templates and we'll place in the v slot, is fun elements for the actual icon and the entity for this is the ampersand, the hash 128293. The final one is our default slots. We will have a span and also give this the name, which is going to be default. Okay, then we can find our outlets and the first one was for the sales total. Into this component place in the slot for our icon location. It has the name of icon. Then we can leave our second slot for the default location. Finally, the bestseller opponents will do the same as the last two components. Adding our slots, placing our name of icon and again, this is the default content 2. Good. We can see in the browser we have all of our three icons and we still have the content which is passed down. The final thing to look at is a shorthand, which we can place on our v slot directive. If we go back to the overview, all we need to do to shorten this is to replace v slots with the harsh. We can do this for our icon and also the default slot 2, replace all six of these. Save our file, and everything still works as before.
66. Scoped Slots: This now brings us on to Scoped slots. This is where things begin to get a little bit more interesting in terms of accessing our data. In this overview.view file, currently we are passing it down dynamic data to these child components, or if it was the other way round and this data lived inside of our child components, what we wanted to access it inside of this file. Well, we can do this with Scoped slots. Earlier we added a sidebar components, but we've not yet added any components. So let's go over to this and we can begin to work inside of our sidebar.view, placing our template, this is going to render out our free links inside of the sidebar, and also give us a chance to look at Scoped slots. Placing the navigation and unordered list, then we can loop through our pages with the list item. We haven't created these yet, but we'll call this pages, so we'll loop through page in pages, also bind a dynamic key. This is simply going to be the page, just for simplicity. Inside of here, place it in a link element, and this is just going to output in between the double curly braces, our page. This is all we need for the template, so going down to the script section, we can setup our data property for all pages. Pages is going to be a pretty straightforward array, so we'll just start in our free strings. The first one was Overview, we have Orders, and also Best Sellers. This is going to be rendered out and registered over in our App.vue. Head over to our script and we can first import our new components. This is the sidebar, which is available from the path, which is dot slash components. Then forward slash sidebar. Register this inside of the components, then we can use this up in our template. The place to do this is going to be inside of the aside section. We can remove our free existing links and then place in our sidebar. Good, now we have our free links still showing, but now these are from our sidebar components. But go to our sidebar.vue components, we have this link just here, which is our dynamic data, which lives in this child components. As an example, imagine if we will use this navigation multiple times in our application. We may want this link to be styled differently in different locations or have some different unique layout options. As we already know, if we were to cut this out and then places in between our sidebar, this won't work since the scope of this page is inside of the sidebar components, not this app.vue. If did want to handle this, we would need to make use of Scoped slots to allow us to pass data back loop to his parents. First in the sidebar.vue, we first pass in the slot components where we want this data to be displayed. We want to now pass is page variable backup to the parents. The way we can do this is by binding a dynamic attributes. I don't want to keep this consistent and call this page. These attributes are called slot props. We can now access these attributes up inside of the parent components, which is the app.vue. First, we're going to wrap this link inside of a template and assign it the default location. Just like we looked at in the last video, we can create our templates, and then use the slots or the shorthand, which is the hash. Then give this the default location where we can now paste our link back inside. This looks pretty much the same as the last video, but the difference this time is here we're going to pass down a value to v slot. This value is a variable name, which we can call anything which you want. To keep this descriptive, I'm going to call this slotProps. Now by using this variable, we have access to the data which had been passed up from our slot. Now we'll have access to this page. Now instead of directly accessing our page, we need to first access our slot props, then the page attributes. We see straight away that our free pages now appear in the sidebar, so we now getting this data from our child components. Alternatively, rather than accessing all of our props on this variable, we can make use of JavaScript destructuring. We can pull out any of our variables or any of our attributes, such as our page. Now we can access this directly in our template. This is how a slot prop works. You may thinking with all of this, why do we have another way of passing data when we already have all props and emits. Well, this is all about giving us as a developer total flexibility. Scoped slots open up the door to so many different things. For example, if this page had user authentication and we had access to our user from our data. As an example, if we had a data property just like this, and if this example had a user objects. If you only wanted to show certain links, if the user was logged in, you will then need to pass this used objects down to the sidebar components via props. However though, using Scoped slots, we could do this exact same setup and we can check if the user is present by placing this v-if on this parent components, we can check if the user exists before rendering out any of our links. A user currently exists. If we change to null, this will then remove our free links. This is just one example to avoid those passing down props to the child components. We can also remove this since this is not needed. Also another example, if we wanted to rent out this component in multiple locations, we could duplicate the content just like this and we could add different classes to each one of our list items. For example, this could be placed inside the sidebar. We'll maybe want the styling to be different for the header. This will give us the flexibility to maybe add a different color links, we could also place the header navigation horizontally across the page, the sidebar one could be vertical and we have full flexibility to do whatever we want to keep both of these unique. Let's remove this and round off this video by adding some styling to our free links. [inaudible] this over in the sidebar, create this style section. First of all, let's go for the unordered list. We can remove the bullets by setting the list-style to be none. Then it resets any default padding to be zero. Then we can add some space in it to these links with the list item by suddenly padding to be 10 pixels on the top and bottom, and then zero on the left and right. It got some spacing between these three links.
67. Orders Page: So far we've only been working on this Overview page. Soon we'll make use of dynamic components, switch between these three pages. But first, let's add our content to the Orders and Best-sellers. The Orders page is going to be made up of two sections. For the first section we'll place in the sales total components so we can see the total sales value. Then below this will create a new section which is going to display all of our orders. Let's go over to our Orders page, open up the sidebar, and this is in the View section. Our first section, just below the level three heading. This is going to be a slot which will output the content of our sales total components. Let's give it a name of total. We'll pass this down via our template in just a moment. The second section, is going to be the wrapper for our second slot, and this will be for the list of orders. Place in our second slot inside here. We don't need to give it a name since this can inherit the default value. Next over to the App.vue. We can first begin to work on this Orders page. We're going to comment out this overview, and also the registration, and then replace this with our imports for our orders. This one is in the Views folder. Registers this inside of the component section. Then back up to our templates, lets comment out overview section, and replace this with our orders. We will be passing this slot contents so this needs the opening and the closing tag. We have two slot locations, so this needs two templates. These slot attributes, for the first one, to be for the total. The second one is going to be for our default content, which is going to be our list of orders. This first template section is going to contain our sales total components, just like we used over in the Overview. If we take a look at this section, this also contains slots to. Here we're passing down our icon, and also the total sales value. To keep this consistent we can copy this full components section, then make use of this over in our App.vue. Paste this in. This will also need to be registered and imported. Let's do this now. Import our sales total, dot slash, and this is in the Components. Then we grab our sales total. Then finally, register this component. Say there is someone else here inside of his orders page, will now have the component displaying. We can see the icon but we have not yet passed down our dynamic content, which is the total sales value. Let's do this now, if we go back to our templates inside of the App.vue. Earlier when we created the Overview page, we passed down this total sales value as a prop. This was just some dummy data, but now we're going to grab our real data, which is provided with this project. To grab this inside of the sidebar, take a look for the Orders.js file. We can now import this, and loop through all of our orders to extract all of the data which we need passed down to this components. Make use of this, we need to first import this file, so we can use this inside of our JavaScript. We can import our orders from our Orders file. This is a named import inside of these curly braces. This will allow us to import a single member from our Orders file. A single member we want to import is this orders variable. We can import this because we've exported this as a constant. If the Orders file did have other exports we could also import them individually inside here to, but we only have this single orders. Then what we need to do is to scroll down and create our data section, so we can make use of this in view JS. Place in the orders which we've just imported. Now, we have access to this inside of our template. To output these correctly inside of our template, we first need to do two loops. The first loop is going to loop over each one of our orders and then for each order we need to loop through all the items inside of this order. Let's do this now, over in the App.vue, scroll up. Inside of this order section, look out for this second default template. It's inside here where we are going to create these two loops. First, a wrapper div which will have the class of order, underscore, wrapper. Next, inside of this wrapper, a second div which is going to surround each one of our orders. To loop through these orders, we'll loop through all of the orders data property. This will also need a dynamic key which we can grabbed from the unique id, which is available for each one of these orders. We can access this with order.id. Now, to structure these, let's add some text at the very top, and this is going to display the order number. We know we already have access to this order number with order.id. We can place this inside of the double curly braces, and then blow each one of these order numbers. Create an unordered list, which we are going to look through each one of our sold items. The wrapper. Then we'll use the list item to create a v-for loop for each one of our items. We can start each one of these in the variable which is item, in order.items. Finding a dynamic key. Now, we need to be careful with the key for each one of these. If we take a look at each one of our orders, remember that the items array contain the same product in any one of the orders. Therefore, we could make use of duplicate ids. Now, if we're using a database, we could generate a unique id. But for demonstration purposes, since we're not using a database, and don't have access to a unique id. What I'm going to do is also to access the index number and combine this with the item. Just after the item, grab the index. Places this in as the key. If this was a real production site, this is probably something to avoid. You should really try to generate a unique id for each one of these products. For demonstration purposes, this will be just fine. Inside here will create a P element which is going to output the item.name. Then we can also output the item price, just afterwards with item.price. Give this a refresh, you will now see a list of all of our orders, and these are now formatted as a unordered list. We finish this off over to our Orders.vue file, where we can add in a little CSS. First of all, the unordered list [inaudible] bullets with list-style, lets set this to be non. The font-style of italic. Then the wrapper for each one of our orders, which hard at the class of order. I'm padding to give this some spacing of 10 pixels. Also, to give this some spacing on the top and bottom we can add some margin of 10 pixels on the top and bottom, and then zero on the left and right. A boarder for each one of our orders of one pixel. A solid line. I'm going to go for a gray value of DDD, border-radius of five pixels. Save this and refresh the browser. Now, we have all of our orders now in place.
68. Best Sellers Page: Next up we have our best sellers page, which we're going to work on, and this is inside of the views folder. Currently we just have this level 3 heading, but what we're going to do now is to create a table with all of our data, which we're going to get from our orders.js file. To get this, we're going to create two loops. The first loop is going to loop through all of our orders array, and this will give us each one of these orders. Then our second loop inside will then loop through all of these items, and then we're going to grab each one of these items and push to a new array. This is what our new array will look like, we'll extract all of the products from our orders and then push them to this new array. In addition to the ID, the name, and the price, we'll also add in a quantity field too. This will avoid us having lots of duplicate products. Instead, when we come across a product inside of our loop, which is already inside this array, we'll increase the quantity field by one. This is the first step, and the second step after doing this is then to reorder this array with the quantity highest to lowest. Let's go over to the app.vue and we'll create this as a computed property just under the data, set this up, and the computed name of sortedItems. The first step is to create our new array called sortedItems, and we'll initially set this to be an empty array. The next step is to create our first loop, which is going to loop through all of our orders. We have this stored above in the state properties, so we can access this.orders, and then map through all of these orders, rising a callback function. Each item in this orders, we'll store this inside of the variable called order. As we just mentioned, we'll grab our order and then we'll create a second loop inside, which is going to loop through all of the items. We'll map over these, and then run a function on each one of these items. Also, before we go any further, remember computed properties will always need to return a value. So we're going to return our new array, which is sorted items. Make sure this is outside of our loops. We're going to return this value. Backup inside of this loop is where we're going to push each one of these items into this new array, but just before we do this, we first need to check if this item is already inside of this array to avoid any duplicates, and the way to do this is to access our new array, which is sorted items. We can call the JavaScript find method. This will run a function for each item inside of our array, storing each item inside of this variable, which I'm going to call sorted item. This function will be responsible for checking if the sorted item.id is equal to the item id from this current loop. If the id of the current item which we own matches any of the IDs which are inside of this array, this will then return the current item. We'll then store this inside of a constant called item exists. Then we can create a if statement just below it to check if item exists is true. If it is true, we have a duplicate item and all we want to do is to access this item, which is item exists, and then select the quantity field, incrementing this by one and then we can return out of this statement. If this item doesn't exist, this means this is the first time this new item is going to be pushed to our array. So what we're going to do is to create a new object called new item. This is going to be equal to our item which we own, and we'll also add in the quantity field too. The quantity will be an initial value of one, and then using the JavaScript spread operator, we'll also pass in the value from the item too. We now have this new item and the final thing to do is to push this to this array. Just after our object, grab our sorted items array and then access the JavaScript push method, which will push our new item. Let's test this out. We can access our sorted items computed property inside of the double curly braces, so just anywhere in our template. There we go. Now rather than having each one of our products listed individually, we've now increased the quantity. One, we have cubic items. We have free T-shirts, we have two of our small custom shirts, we have, four, the hoodies, and then one of the hats. Now this is working. The final step is to rearrange the items inside of this array by the highest quantity sold. Currently we have the quantity free. Then we have two, we have four, and then we have one, so this is not in order. We can rearrange these just before we return these from our computed section. Just above the return statement, access the sorted items, and then we can use the JavaScript sort method. The JavaScript sort method will loop through all of our sorted items and then run a comparison function. This comparison function will take in two variables with the name of our choice. I'm just going to call this A and B to keep it simple, and these are the two current items which are being compared. But I'm going to check if the quantity property on item A is greater than the quantity on item B. Using the ternary operator, we'll then set the index position, if this is true, to be a negative one, if this is false, it will then be positive one. Remember, when dealing with arrays, a lower index number will appear first in our array, so setting this to be negative one will push this higher up in the array. We can see straight away in the browser that the quantity of four is placed first, we have the quantity of three, and finally, our vue hat which is the quantity of one. Now we have all the data we need, we can now import the bestsellers page inside of this app.vue. We'll also register this and display this inside of the template. Let's duplicate this, and this one is for our bestsellers. Add this to our components, but I will comment out our audit components and also the oldest section from our template. Grab this all the way down to the closing tag, comment this out, and we can now place inside of our template our bestsellers. This is just for our simplicity, so we can focus on one component at a time. We see an error inside here that the sales total component is not being used, so we can also comment this out. Also the registration, even this with our bestsellers page. [inaudible] top, we're going to remove this computed section, and then set up our table inside of the bestsellers. Placing this inside of the opening and closing tag, so this will be passed as slot content. First of all, we'll set up our outer table section, the table heading for our titles, and then our first table row. Inside of this row, we'll place in our first piece of data inside of the table heading, and this is for the quantity sold. The next table heading is for the product name, the third one for the product ID, then finally the product price. After the head section we have the table body, and it's inside of this table body section we'll provide all the data from our computed section. To do this, we'll create a new table row, and we need to repeat this for each item inside of this computed section. [inaudible] For Loop. We can add in our item in sorted items. So we could either access the full item like this or we could also use the structure and to pull out the id, the quantity, the name, and also the price. We also need to bind in a dynamic key, and the dynamic key for this can be the product ID. These four variables can now be output as table data. Inside the row, the table data for the first one is for the quantity, and we'll have the name, the ID, and then the price. This is all the slot data we now need to pass, so over to our bestsellers page in the views, and we're going to output this as a slot. There we are. There's a word table heading and also our table data, and now these items are now listed from the highest to the lowest quantity. Just to finish off this page, head over to our bestsellers component where we can set up some basic styles for this table. First of all, the table wrapper, the border of one pixel, a solid line, and the color of DDD. Align the text with text aligned center. The border radius of five pixels. Then we'll also give this some space in with some padding values inside of the table headings and also the table data too. This now spaces out all of our data inside of our table. This is now our bestsellers page completed. In the next video, we'll take a look at how we can switch between these three pages by using dynamic components.
69. Dynamic Components: When working on our three pages up to now, we have been commenting out each one of these and then adding a new one to work on. Large apps will often switch between pages using a router. I will do this for our work final project. For this simple case, we're going to setup a dynamic components to handle this, open up the Vue. We're going to comment out our final component, which is the bestsellers, then replace this with the component elements. To declare which page or which components we're going to display in this component location, we can then bind the is property and set this equal to a date property called selectedPage. Just going to call it anything you want as long as it matches up with a date property which we're going to create now. At this end, we can initially set this to our first page, which is the overview. The selected page is going to be updated when we click on any of the sidebar links. One thing to know when using the dynamic components is Vue does not know which component we're going to be using, so it will not throw an error if we have any imports, which we're not using. As we can see here, we still have the import, the bestsellers. We're not currently using this in the template. To make all of these components available, we're going to uncomment out the overview, the orders, and also the sales total, and the same when we register our components too. Since we've set the selectedPage, the overview, we now see this inside the browser. Now, update this page we can list now for a click on any one of these sidebar links, up to the sidebar section and inside of our link, list now for a click on any one of these three pages, we will set the selected page to be equal to the page which is stored inside of this variable just here. We can have the same side of the back ticks as a template string. Let's give this go in the browser. Refresh. As you can see, the overview is working fine, but we don't have any of the data passed down just yet. The orders, again, we just see the title because we have not passed down any dynamic data just yet. The bestsellers, we don't see anything when we click on this page. Let's go into the developer tools and see what's going on. Into the console, it says the tag name provided of bestsellers is not a valid name. Remember, we are trying to load any one of our components based off this page name. Try to pause and take a moment to think about what could be causing this error. If you have not figured out yet, the answer is the space between these two words. We are trying to link to a page, which is best, and a space, and then sellers. But if we take a look at our page name, this has no space in between. The fix is, we need to remove any white space when calling these components. We can set this will be inside of our dynamic components. One way of doing this is by using a JavaScript string replace method. This will take our existence string which is selectedPage. We can then modify it to remove any white space and then return a new one. Call the replace method. Then we can place in a JavaScript regular expression in between these two forward slashes. When regular expressions, the way we can match any white space is by using the backslash and the S, separated by a comma, we'll then replace this white space with a empty string containing no space at all. Let's save this and try this out. The overview, the orders, and when also the bestsellers page is appearing too. Great. We can now switch pages, but a lot of the content is missing. This is because we have not passed any props or slot content to these three pages. Dynamic components can also accept both props and slot content if we wanted to. But remember, each components has different data. For props, we would need to pass all the props we may ever need inside of this one single component. Also, if passing a slot content, it is also difficult too since each component has different data. Although in the next section, we will solve these problems by using a technique called provide and inject.
70. Section Intro- Provide / Inject: While working with Vue.js and passing around data, we need to pass down props down to each components. If we have a child component which is maybe two or more levels deep, we need to pass all props from our parent and then down to each one of the core components on the way. This is really useful for keeping control of our data, but it's not always ideal. This Vue.js has us covered with a technique called provide and inject, and we'll take a look at this during this section.
71. What Is Provide / Inject?: When working in an ideal world, our data or our state won't have to travel very far, such as has this application here which communicates with the cal components. This is pretty straightforward. We have many options which we have looked at in the past, such as passing down data as props and emitting them back up with custom events. Also the option of using the slots too, even this pretty simple application is starting to get components nested deeper than one level. This view JS application switches between three pages and our overview page has nested inside our free child components. If our data was in the main app.vue, we could use props to pass it down to all of our pages. Then we could also keep passing down the props of the components nested inside, and this chain can keep going for as many levels deep as we need to. This does, however, cause a lot of extra code. We'll need to register them as each component, validate these props, and then pass them down to the next child as an attribute. To pass them back up, we use custom events. Again, this is really good for simple applications, but for many levels deep, it can become a pain. It would be so much more convenient if we could bypass all of this and provide the data directly into any child component. Vue does have a state management library available as a separate module called Vuex, which is really good and also maintained by the vue.js core team. This provides a central data store, but it also does come at a cost of needing to install and setup a new package. It is reasonably easy to learn and also to use, but it is something extra we don't quite need at this stage. Also, probably better used on larger scale applications. Alternatively, Vue also has a built-in feature called provide and inject. The concept is, we have a provider which is used to provide data to the rest of our application and this is placed in any parent component. Then any child component below at any level can be injected with this data. Not only this, but a bit like when we emitted custom events back up the chain. We can also change state in the parent by triggering methods. This is provide and inject, which we're now going to take a look at during this section.
72. Setting Up a Provider: Let's now take a look at how we can setup a provider. In this App.vue file, we have this dynamic component which we recently setup, and this is going to be responsible for rendering one of our free pages, and each one of these pages requires different data. Rather than passing down all of the props to cover all of these situations, we can instead use provide and inject, place the correct data directly into our chart components. First, let's start with our overview page, which is our top one, just here. Inside here, this is rendering our free components, we have the items sold, the sales total, and the bestseller. Remember this is using slots and we're using this data which is passed down as props, so instead, what we're going to do is to move this data directly over to the components and then we'll access the data we need using provide and inject. Let's begin with this item sold. We grab the slot content from in-between the template, as well as go for the icon, and then into the items sold components, we can replace our slot, where this is output. In the overview, we have the second piece of slot content, which is for the default, and then replace our second slot. We can now clean up this items sold component, and we have our templates and then we can do the same for the sales total, without the icon, places into the icon slot, and then same for our total sales value, paste this into our default slot and of the components, and exactly the same for our third component too, the icon for the bestseller [inaudible] and also the content too. Then clean up our bestseller component. Now, this leaves us with our free chart components with dynamic data, which we now need to pass down. This we can add a provider into any one of the paren components and still isn't going to go over to the App.vue file, and then into the script section. The provide option is pretty straightforward. All we need to do is to provide an object. This is another place where we can provide any data which you want to be available in any of the chart components. We already know which data we need. We need the quantity of items sold, we need the total sales value, and also the bestseller too. Let's set these up. First of all, the quantity of items sold. We can place in any value inside of here for now and very soon we will obtain this value using our orders file. Next, we need total sales value. This is going to be any value again, and the third one for the bestseller, which is a string, you want to go for the Vue hoodie in medium, and this is our provided data. Next we'll look at how we can access this data in our chart components.
73. Injecting Data: Inside of our app.vue file, we now have this provide object which will provide all of the data inside to any of the child components. This data is not automatically inherited in the child components. Instead, what we need to do is to go into any components, where we want to use it and add an injector array with the properties we want to accept. Let's begin in the items sold, where we need this quantity of items sold value. We can start to have a script, just like when we accept any props. Inside here, we're going to setup an array called inject. Then we can pass in the value we need, which is quantity of items sold. If we save this, we now see the value has been updated inside the browser. This works because both of these names will match. Let's grab the script section, and we are going to place this also in the sales total. This also needs the total sales value which is passed down. Save this, and this will now appear. Then finally, the bestseller. There we go. But as we already know, this is all just currently static data, but it should be dynamic and based of our orders.js file. To do this back in our app.vue, we are now going to set these first two values to be dynamic by setting up a computed property. First, let's go for the quantity of items sold. They're just underneath our sorted items, separated by comma, we will set this up. Now this leaves us with a couple of options. We could go directly into our orders.js file. Just like we've done previously, we can loop through all the orders and then all of the order items, or alternatively just above this, we already have our sorted items, which is an array of all items which we've sold. Instead we can use this, but first we'll create a new variable called quantity, and set this up to be an initial value of zero. Then to obtain this, we can grab our computed property which was sorted items, loop over this. Then create a function for each item. We're looping through all of those sold items and we have this object, just like we see here. Remember that each one of these objects has a quantity field. What we now need to do is to update our quantity with the quantity field from this item. So grab our variable, and then we can add using the += operator, the item.quantity. This operator will auto-renew item quantity to the existing value rather than replace it. Then return our quantity from this computed value. Now we can use this name openly provide objects and replace the hard-coded value of 54 with the value of this dots quantity of items sold. Next, the total sales value. I'll just [inaudible] add a comma, and setup our second computed property. Just like both will create a new variable, this time called total, and set this to an initial value of zero. Again, for this one, we have a couple of options. We could once again live through our sorted items. We could do something such as multiplying the quantity by the price of the item, or we could go directly to our orders array. Then loop through our orders, loop through the items, and then add together all of the prices. This is what I'm going to do, which is loop through all of our orders. We do this.orders.map. We will grab each individual order, and then loop through the items property. Select each item. Then for each item in one of our single orders, we will then grab the price field, and update our total, and then return back this total value. Now, back up to our provider, move this. We can set this value to be this.totalsalesvalue. Give this a save and off to the browser. We are now seeing no data inside of here. Let's go into the inspector and see what's going on in the console. We got an error saying we can not read the property of quantity of items sold. Now, the reason we are seeing this error is because when referencing any data properties or dynamic data on this provide object, we need to instead return this as a function. This function is setup just like the data property above. This is going to return all of our values which include dynamic data. This was completely fine before because all we were outputing was static data such as the string and also our numbers. If we turn this into a function, just like the data, we're going to grab our three values and then return these back from all function off to the browser. We now see the items sold, but we're missing the sales total. Let's take a look. We have this.totalsalesvalue of the quantity of items sold. I think we maybe just need to place this in the computed section, so we cut this. If we take a look at the closing brace for the computed, we just need to place this inside of the right section, separated by a comma, and there we go. Lastly, we have this top-selling item which is currently passed down as a string. Now, we're going to fix this by again accessing the sorted items, which is an array of our top selling items. Remember this is the highest quantity sold first. All we need to do for this is to access the very first item inside of our array. I'll cut out the string. We can access this.sorteditems. Then access this first one which is the highest selling quantity. This will return our full object for this item. We can now narrow this down in the best-seller components, and directly access the name property. Now, our free injected values are now in the correct components.
74. Mini Challenge: Update Orders & Best Sellers Pages To Use Provide / Inject: We've now converted our overview page to use provide and inject. What I would like you to do now as a challenge is to go ahead and do the same for the orders and the best sellers pages. Now in the App.vue, we still have commented out our bestseller section and scrolling up we also have this orders section here too. We've already done the overview so we can ignore this. What I would like you to do is to move over all of the slot contents, which we have for each page, into the individual components, just like we did with the overview where we extracted all the contents into the individual components. Then fill any sections which have dynamic data such as this path, set up by provide inside of this App.vue and then inject the data into the required components of ages. They'll give us a go and next I'll walk through how I did it.
75. Update Orders & Best Sellers Pages To Use Provide / Inject: I'm going to kick this off with the Orders page. If we scroll up to the commented section in the App.vue, let's begin with this order section. I'm going to uncomment this out, so it's a little bit more clear what we are doing. Inside of here, the first section or the first slot we have is this total section. This contains the sales total component. What we need to do is to cut this out, and then go over to the Orders page, and place this in place of our total slot. The Orders.vue, this is the slot with the name of Total. Paste in our components. Now, we are making use of a component. What we need to do is to also import this into this file too. Let's create a script section. I'll just blow this. Create our scripts section and just above the export default, we'll import this single component which was sales total. The file path is../components, and then into the sales total. Set up our component subject. Passing in this sales total. Next, over to the App.vue, we still have this default section. If I highlight this, we could now grab all of the div, which is nested inside with the class of order wrapper. Put this out, I'm going to go clean up this orders components and then paste this into our slot. From here we consider this section that needs access to all of the orders so we can now pass this down using it provides and inject. First, our provider, which is in the App.vue, set up the orders, which is equal to this two orders which lives on our data property. Over to the Orders.vue, we can now inject this into our script. This is an array with the single value of orders. This now places our orders back into this section. We'll still see this because we have over in App.vue, we still are putting in these orders components. I'm going to comment this out and click on the Orders link. Everything is working fine. Next to our bestsellers page, again, let's uncomment this out so it's more clear. This one's a little bit more straightforward. We only have this table. Code this out. Over to the Bestsellers.vue page, and we can replace the slot contents with our table, this also needs access to our computer property which is sorted items. Let's pass this down in our provider. Sorted items, which is going to be equal to this got sorted items, which is a computer property over to our component, which is bestsellers Set up a script. All we need to do for this is to setup our injector array, which is going to accept the salted items. There's our table and we can clean up our App.vue by removing any of the old components, leaving this dynamic section. We don't need the bestsellers, we don't need the orders, and we don't need the overview section either. It was a test, we have the overview of the orders and also the best sellers too.
76. Updating the Provider From a Child Component: Along with use provide and inject to pass down data, we may also need a way to change from state in a parent components from within a child. We know about how to do this by emitting a custom events. But when using provides and inject, the way to approach this is to have a method in the same file as the provider, and then we pass down to the child components, a reference to this method which you can then trigger. We are essentially creating a method in a parent components and then calling it from a child who see an action. We're going to figure the switching of our pages from HR components inside of the app.view. Let's clean up this sidebar section and remove all of the slot contents. This will then remove our link and we can now place this back into our sidebar.vue file instead of our slots, since we now not receiving any slot data, we can replace this with a link. This link will simply output the page from our loop, and we now need to setup a method in the app.view which is triggered when we click on any of these links. Down to our scripts will setup the method section, and this method is going to be called changePage. This will also need to take in the page which you want to switch to, and then all we're going to do is to change the dates property of selectedPage to be equal to this value. We can now pass down a reference to this method inside of our provider. I'm going to keep these consistent and also call this changePage, which is equal to our method of the same name. We want to toggle this change page down inside of the sidebar, and just like we would pass down any data or state, we can also add this into an inject array. Now this page has access to all changePage method. We are now going to use our link inside of the template to listening for a click and activate this method, so this now for a click, that's now a method name of changePage. Remember, this also needs to take in the page which you want to navigate to, and we'll have access to this with this page variable. Let's try this out over to the browser, have the overview, the orders, and also the best-sellers which has now been triggered from this child component. You finishes off we can change the cursor to be a pointer and we hover over any one of these links. A li styling is already setup and all I need to do is to add at the cursor to be a pointer. Now this all works great, but there is something to be aware of, which is by default provides and inject is not reactive. This means if any of all provided values change, the child components will not be updated. There is couple of ways we can view this if we do on reactivity, and our app has a component structure which is not too deep. We could just use props and emit. We could also use provides and inject, set the initial values for child components once the upload, or alternatively, if we really wanted to use provides and inject, but reactivity was important. There is a way to do this when we combine this with the view free composition API, and this is something which we're going to discover soon.
77. Section Intro- Introduction To Routing: When building our applications, we often have the use case to switch between multiple pages. For this Vue also has us covered by providing a router package. If you router is created and actively maintained by the Vue.js core team so we can be confident that it's going to be compatible with your version of Vue, and also any updates, too. Alongside, if you're switching between our pages, the relative package also has lots of other great features too, which we will discover in this upcoming section.
78. What We Will Be Building & Starter Project: Welcome back to this brand new section. For the upcoming sections, we're also going to have a new project called Creative Cards. This is going to be a card editing application and we can see from the start screen here we have the header section, we have some links to popular categories. Then we have all of our available categories listed below. This is all generated dynamically from the data which we have in our application. We can click on any of these categories and see all of the available cards, which match this current category. If we select all, we see a link for all of the available cards, which we can now select to edit. If we click on any of these, we are then taken to the edit view. We can select all the pages. We have the front, the inside left, inside right, and also the back too. From any of these views we have the intersection of on the right. We can select new images which you want to use for all backgrounds. We can interchange the position of this image. We can also remove the image too if we don't want to have one for this particular page. We can also edit this text and any changes which we make are reflected in the preview over on the left. Over and over this we also have some access to all options. We can change the font type, we can change the font style. We can change the color and also make it bold, italic. We can increase the size of the section. We can set the horizontal and vertical alignment too. This is the exact same for any one of our pages. These are all fully editable. We're not stuck with the defaults which are provided on the first click on this card. This will also be linked to a database too so we want to be happy. We can then click on the Place Order button and it will send this order through to a database. Along with a database, we'll also explore some new features in the upcoming sections, such as serverless functions, we'll take a look at the vue router, the vue composition API, and so much more. As with the rest of the project, if you download the creative card starter from the GitHub repository, if you already have the folder from the start of the course, you can head into there and open up the creative card starter. I've already navigated to this inside the terminal and opened this up inside of visual studio code. To begin, we have a pretty basic view CLI setter. But we'll just stay few additions for this project inside of the index dot HTML. I've added a link to Google Fonts. The source directly contains inside of the assets folder, some icons and also some images which we're going to use as a starting point. We will have a router folder and we'll take a look at this next. But probably the most important part is this data dot js. This is an array of different cards which we're going to use as a starting point for this project. These are the sections which are all fully editable. When we change these sections inside of here, this is an array which contains objects for each one of our cards. I have the ID, the name, and also the categories which this card belongs to and these are all used to generate the categories which you see on the homepage just here. After this, an array of pages, we have the front, the inside left, the inside right, and also the back, a default background image position. Then we jump into the card sections, this is the tech sections with all of the data. We have the height, the color, and also the font family, along with all of the other options which are all editable from our menu. The only thing we need to do now is to head over to the terminal and run NPM install using the NPM I command. This will install all of the node packages which we need for this project. And once this is done, we'll now move on to the next video where we'll install the vue router.
79. Setting Up The Vue Router: If you've never used a router before, it is a package we can use to basically switch between our pages or views. Since we're dealing with single-page applications, we don't have the traditional way of changing pages by requesting them from this server. Instead, we rely on a router which will map a URL to one of our components. This is a much more flexible and advanced way of navigating our applications, the only way we looked at in the previous project when we set up a dynamic component for this use case. Its not just about switching pages though, there is a lot more features, we'll also just go over to. Also the vue-router package is officially supported and maintained by the Core Vue.js team. So it is deeply integrated, supported, and also kept up-to-date too. There is a CDN link, but since we're using the view CLI, installation is really simple. To keep our packages on the same version, the starter project which we have here, already has this router installed. We can see this over in the package.json into the dependencies. We've the vue-router which is really simple to add, and if you do want to add this a different project which you are creating on your own, yo u can go over to the terminal. Remember, this is already installed, so don't run this command. But if you were using this on a different project, you could install with the vue-router, with the command of a vue add router. This will add the vue-router as a CLI plug-in, and also give you the option of setting up a history mode, which we'll look at later. For this particular project, we have not yet set up history mode when include new router. Once the vue add router command has finished running, if you go into the source directory, this will add a new folder. The new folder is this router just here, which contains a Index.js file. If we take a look at the top, the first thing we'll do is to import the packages which we need from the vue-router, we'll talk about the history modes very soon, but this is the package which we need to create a new vue-router. Then below this, we import any component which you want to switch between from our router. Here we have the import from our Home components, which is been added with the Vue CLI. It's created a views directory with the about, and also the homepage to get started with the router. Next, we have a array of routes which we want to use for our application. Each one of these objects contains a path. We have the home link and also the about's link just below. Each one of these links will map to a particular component. This one is linking it to our home components. If we take a look at the about component, this is a little different to the one just above. This one is using code splitting, and we'll take a look at how to do this later on. We then create a router object which contains any configuration which we need, such as the history which we just mentioned before. We also pass in our route array too. Finally, we export our router file, so this can be used in our application of to the main.js. We then import this exported router just here, and then add this to our application before this is mounted to the done. It's an action that goes to the terminal and starts up our development server would NPM run serve. Open this up. We now have two links at the top of the page, about the home, which links to /, then also the about this page too.
80. Router link and router view: We now have two links at the top of the page. We have the Home which links to forward slash. Then also the About Us page too. This is in much need two routes which we've seen in the router file just here, and then mapping to our components. The Vue CLI is automatically added in these two routes photos. But generally, we need to add these in our cells. Also, these links which you see here, if we go over to our App.vue, the CLI has already added these two links in photos using the router link. Router link is a component which accepts the two prop as an attribute. It's inside here where we pass in the route which we want to navigate to. We can place these router links in any components or any page which we want to. Using this router link is also preferred over using the regular HTML a elements for various reasons. If we take a look at the outputs of any browser developer tools, right-click "Inspect", open up the body section, and take a look for the app in navigation. We see in place of our router link, we have this a element, our Home, and also the About link. There is however, a difference between the a element which has been added automatically, among which we add ourselves. When we have history mode set, which we'll take a look at soon, this link which has been added in will intercept any clicks and prevent the browser from refreshing the page automatically. Do try to use a router link where possible, rather than a HTML link element. Also, if you have used the router in Vue version 2, you may have also used the Tag prompt just like this. This allowed us to change the element which wrapped our link, any other HTML element such as a list item. However, do be aware that this is not available in the latest version of the router, so you will need to remove this if you have it on any existent projects. We're not just limited to passing in a static string like this, about two prop, we can also pass in dynamic data too. For example, we may have a user just like this. Let's create a script section with our export default, with our data section, and then we're going to return a user. This user can be an object with the name and also a user ID. We can then make this dynamic with the colon and then pass in a template string inside of the [inaudible]. We may want to go to a route, which is forward slash user, and then the user's ID. Give this a save, refresh, and now if we click on "About", this sends us to user/12345, and this gives us an ID of the flexibility which we have when selecting new routes. We can also extend even further by passing in an object. Rather than our path just like this, we can pass in our object. It's more simple, we can pass in a path which we want to navigate to. Again, we can pass in a dynamic path or we can just add in a regular string. This will also need to be dynamic, so we add the binding. Same for our second link, we can add in the path. Now, since we have an object, we can also add additional properties too. For now, give this a save and we can refresh the browser, link to our Home and also the About Us page too, or alternatively, instead of referencing a path just like this, we can also go off the router and into the index or js, and then use the name which we've already provided inside here. We have the Home and also the About Us names, either one of these can now be used over in our router link. Inside of our object, we can replace our path with the name property. On the first one was Home, and the second one was About. Let's test this out, refresh. It still navigates to our two pages. Just bellow our links, we can see the page content just below. The reason we can see this is because of this router view which has been placed under our links. This acts as an [inaudible] for the components which we are currently viewing. We can place this anywhere inside of our template.
81. Params & Queries: The router package has more to offer than just switching between components. It can also be used to pass data in the form of params and also a query string too. First, let's take a look at params or parameters, which are variables for a given route in the router link, which we have just here. We can add our params inside of this two object. So just after our name separated by a comma, add in our params. Inside of this object, we can add in as many parameters as we want. But now I'm just going to add in a user ID, which is the name. The value is going to be equal to user.id, which we can get from our data. Here we're dealing with this homepage. Let's go over to the Vue and then into the home.vue components. Inside here we have multiple ways to access our routers data. Inside of the double curly braces, we can output this with the dollar symbol router. Over to the home link. This now gives us a lot of information about the router. We can actually see this a little bit easier if we go into the developer tools. So go into Inspect, open up this home components, and this now shows all of our routing information a little bit more structured. We have access to things such as the current route. We can see any queries or any parameters, such as the user ID which we just added. If we scroll down, we have lots more information available for this router, such as any options. We can also see all of the routes which we have set in our router file. We have the home component which is the path of forward slash. Then we have this about one just below two. So this is how we can access all of the routers information. We could also use this to access the [inaudible] params, or instead we could use the dollar symbol route, which will filter out a lot of this information and just give us access to the current route information. We can already see this is a lot more simpler. We can see on this object we have access to our params. Inside here we can access dot params. Save this. Now have access to our user ID. If we just wanted to access this value directly or if you had multiple params, we can also access them by their name too. Now this user ID would just give us access to the value. The router can also handle query strings too. If you've not used a query string in the past, they're a way of providing some additional information inside of a URL. You may have seen a URL which looks something like this. We have a question mark, which is used to separate the query from the URL. Then we'll have our names and our values just like this. Now the user, which is equal to any value. This is one section. We can also use the ampersand to attend multiple query parameters. These query strings can be accessed locally or we can also send them to the server too. A typical use case for a query string is when used with a HTML form. We can see this if we add a simple forms or template. We'll just keep it simple at a name input. Then the name attribute is important. We're going to call this user. This name attribute is the one which is shown in the query string. We'll see this in just a second. We'll also add a location. Again, an input with the type of text. This one can have the name of location. A button to submit this with the type of submit. Now over to the browser, if we add in any name inside here, a location, we can send. This is now added to the URL as a query string. We see the user is equal to Chris and also the location to both this user and also this location is covered from all form from these name attribute. All of it's information will be sent to the server with our form. Let's remove this and all of these query strings were added automatically or may submitted as form. But we can also add them in manually too. If we go to our app.vue and then into our router link, is a setup just like our params, we can add a comma. Inside of our object, setup our query, where we can add in as many query strings as we would like. For example, we could add a token with this about link, save this. Click on the about link, and this is then added to the end of our URL. If we also wanted to access any of this information, we could access it directly from our template too. Back to our home components and still on this route object, instead of accessing our params, all you need to do is to access our query, refresh. We don't see anything inside here since we added this to the about link. We can cut this out over to the about at this end and that is our query. We can also add in multiple queries too and then access them by their name, such as this token. This is how we can access parameters and our query strings. In the upcoming video, we'll take a look at how we can match dynamic routes.
82. Matching Dynamic Routes: URL's are not always as simple as what we have here with our forward slash and the forward slash about route. They also need to be more dynamic and contain data which will change your data which is different for each user. For example, you may have a path which looks like this, you may have forward slash accounts. This path would be simple for the routers handle, we'll just need to map this to an account component. But what if we wanted to also pass in the user ID, that link to a certain user just like this? It would be way too much work to create a new route for every single use which we had in our database, but for this the router provides what is called a dynamic segments which is a way to have part of our URL which may change those two points to the same components. Inside of our routers index file the first thing we're going to do is to import a new component, we have not yet created this but will do this in just a moment. We'll import our account, this would be the same location as just above, it will be in the views folder, and this will be the account.vue. After this, we'll create a path object just like we see here, so open up the curly braces and the path for this, we'll come back to you in just a second. We'll have a name of account and also point to our components which we just imported that service file and we can now create this account components inside of our views folder. It'll be pretty simple for now, we'll just place in a level one heading of my account. I go to the router we can now create our path for this component, so we want this to map to forward slash accounts, and then we want to handle any dynamic sections such as our user ID. So what we're going to do in here is basically pass in a variable using the colon we can give this a name of our choice, this is going to allow us to capture this information if required. In our case we'll call this the user ID, and then over to the app.vue we need to create a router link to link to our new components so its placed at the end, opening and closing tags and then inside of the opening tag. When I did the two prop, I see no object where the path, this will be dynamic so we can place this inside of the back ticks and we want to go to forward slash account and then we can make use of our user objects and link to the ID. So over in the browser, and we also need to add a name inside of here, and we can also place one of these separated in two between each one of these behalf this pipe symbol. Refresh and now click on our accounts and our my account section is now displaying when we link to this account section which includes the dynamic user ID. If we do need to access the data inside of this dynamic section such as this user ID, we can access these weird routes.params, just like we looked at earlier. Up to the account.vue, open up the double curly braces we can access the current route information, the params which now gives us access to this user ID variable which we sat inside of our router file. Also if we wanted to link to this route by the name of an app.view, we can do this just like we did both with this home route so instead of having a path just like this which includes a dynamic section, we can link to the route name which was account then we can pass in the parameters just like we see above which was an object. We need to pass in the user ID, which is equal to the user.id. By this out, the Home, the About, and the Account page is still working too, we could also pass in a multiple params and also have multiple dynamics sections too. So for example, if this account section and then go on to list all of the blog posts for this user, we could then also store any of the individual blog posts into eight dynamic segments and this works exactly the same. If we go to the app.vue, we can pass in a second parameter. This one was the post and we can pass in any unique name into here, refresh, let's try this out. We see our post ID is also available too.
83. Nested Routes: Inside of our App.vue file, we are handling all of our route changes with these links, and then displaying the selected page with this router view. This is all good. Well, what if we did not want all of our vues to be placed in this location? For example, what if we had our Account.vue just here. This also had some related pages, such as the previous orders and also a section to update the profile. We may want these two new sections to be only shown inside of this account vue. But if used case, we have what is called nested routes. To see this in action, we need to create some new pages inside of our Vue's folder. Create a new file. The first one is update profile and the second one is user orders. A basic title will be fine for now. We set-up the template with a level 1 heading which is for the previous orders. We can save this and copy this over to the user profile. We, of course, also needs some links to link to our new pages. So back to the App.vue component, set up our new two router links to update profile. Then, the second one is for our previous orders. As we just mentioned, both of these links are related to our account page, so may make sense to have these underneath the account path just like this. We could have forward slash accounts and forward slash updates. We could also nest inside our profile too. Let's include these with the to prompt, parse in our object which includes the path. This will be dynamic, so we need the backtick. This will go to forward slash account, then into the specific user.id, and then to forward slash update. Grab this. At the same but this one is going to go to orders until we're done with this head over to the relative file where we're going to import our two new components. This next one was the update profile, and the final one was the user orders. Then, we can set up our router path just below. One option is to create two new routes. One which points to our forward slash update and one which points to our forward slash orders. This is not ideal [inaudible] since we still want to display this same account components but we want these two new pages to be nested inside. To deal with this, we can add a children array. This will declare which components should be nested inside of this top-level account page. First, we don't need this post section so we can remove this. Then, separated by a comma, we can add in our children array. Inside here, we're going to parse in a new object, which is a new route, but the difference is the new route which you parse inside here will be what goes on the end of this [inaudible] route. For example, if we wanted a forward slash update, we would just simply parse in the path as update. Then, once the user visits forward slash account, forward slash the userId, forward slash update. We then declare which components we want to render. In our case, it's the page which we've just created called update profile. Next, our second object is going to be for forward slash orders. This will display the component which we've just imported, which was user orders. Let's try this out, refresh the browser. Now, we see our links up at the top, click on update profile. This will only take us to the account components. Still, with the forward slash update section, we could try the previous orders. Again, we have the correct URL and still it's showing me account components. As you can see, we still see this main account component. But what about the two nested components inside? Well, for this, we need to tell a Vue js exactly where we want these two components to be displayed inside of our parents. Over to the Account.vue. Well, for this, we can also pass in a router view component. Now, when we switch between our two child links, we also see the contents of these components too.
84. Active classes: It will also help us better understand which route we are currently on. Vue routes also add some classes to the active link. Just before we see this in action, let's go over to the app.vue and quickly remove our unused params from this account link. We don't need this post since we removed this in the last video, and now if we go over to the browser and go into the developer tools, right-click Inspect. We need to open up this in nav section. This contains all of our links about the very top, and if we click on the Home link, you'll see this class will appear of router-link-active and also router-link-exact-active too. Let's try the about and these class is now moved to the about link. The account, this also gets it too. But if we click on the "Update Profile," we still see our two classes are being added to our Update Profile link, but the account link still has this router-link-active class. The same if we go to previous orders, our two classes are now on our previous orders, but the router link active is still on our account link, even though we have not clicked on this link. This is because this router-link-active will always apply even on a partial match. Currently, we're on /account, the user ID, and then /orders. But since the account link also begins with /account too, this will also be considered a match. Router-link-exact-active though is more specific and it must match the exact path which we're on for this class to apply. If we close this down and head over to the app.vue, when we also setup the viewRouter, it also added some classes inside of here too. We have some classes for our navigation, and also, this class for router-link-exact-active setting the color of our link. This is why when we click on any of these links up at the top, we see this green color, even though we didn't set this ourselves, inside of the style sheets, since this is the exact class, you will only set the green color when there is an exact match. If we were, however, to change this, the router-link-active, since our current path begins with /account, this will also match this account link too. Also the same if we click on previous orders, we see our two links are active. We're going to do this. The style will now only apply to the exact route which we're on. So this is just something to be aware of and it gives us fine control over the styling of our links.
85. Fallback Pages: It's also important to handle what happens if the user lands on the wrong part of our application or onto a page which doesn't exist. We can setup a general catch all components such as a follow four page to display if no over routes are matched. At the moment, if we go to the browser and type in unrecognized URL, we don't see any component displaying in the browser. To catch this, we're going to go over to our Views and create a new page called NotFound.vue. Just a simple template will be fine with a level 1 heading, and this heading is just going to say page not found. If we wanted to, we could also place in links to different parts for app or even redirect back to the homepage if we wanted to. This is fine for this example. Say this and now to access this, we need to go over to our router, and at the very top, we'll import this as not found. Then into our router array, create a new object which is going to handle all of the routes which are not recognized just above. It will also need to take in a path which we'll take a look at in just a second. The name, not found, and also the components which you just imported, which is also not found too. If you've used view to in the past and used a router to catch any unrecognized paths, all we would need to do is to add in a star inside here, and this would then render out our NotFound components for any unrecognized path. However, though in view 3, we need to pass in a custom parameter. A custom parameter is just like the ones we've used above for the user ID, but the difference this time is we also need to pass in a regular expression to match any unrecognized route. Let's add in our custom parameter, which I'm going to call the pathname. A mistakes in the regular expression, which is the dot and the star, and this will match any other routes which are not listed above. I will say this, we see that our unrecognized URL will now render out our page not found components. We can also access these parameters too which we have just here inside of the component. Do this, we access them just like any other router parameters. We open up the double curly braces, access the current route, and then select the params. This now gives us access to this path name variable and also the value too. We could also access multiple path just like this if we added a forward slash and then a second segment. However, though this will just render out a simple string. If we did want to break up the sections and access them individually, we can repeat this and create an array of parameters. To do this, all we need to do is to go to our router and then add a optional star at the very end, and mostly say we see this is converted to an array with multiple values. This now gives us the option to use these parameters individually if we needed to, or even navigate through this path too. We also don't need to much the full URL just like this. This can also be a section of our URL too. For example, if this was forward slash Admin, this would match the admin section, and then we could use the catch-all section for anything which follows. This would then allow us to be more specific, and we could create custom components for different pages, which may be wrong. For this use case, we could then customize our not found page to be more personal for this admin route, we can maybe add in some texts or just no admin page found, and then we could also pass in the router, let the user know exactly which path they are on. Since this is in the admin route, we're not seeing this not found page. What we need to do is to go to forward slash admin and then place in anything after this path or page not found is then rendered, and then also our customer message just below.
86. Setting Up Our Project Components & Routes: Welcome back. We've now had a little time to play around with the Vue Router and see some of its features. Now, we can take what you've learned and apply it to our new project. To begin, we'll clean up this follow four-page by removing the message and then go to our router file and reinstate this to cover all not found pages. Then next, we're going to go into the views folder and create the four new pages which we need for our project. We can remove the about, we don't need this, the account, remove the homepage. We're going to even need NotFound components, we can remove the UpdateProfile and also the UserOrders too. For now, we'll create our four new pages, and the first file which we're going to be accessing is the Admin.vue. The next page is going to be for the AppHome. The third one is for the Category. This is a page to display all of our cards by the category. Then finally, the Create.vue, which will be the vue which we use to edit the card. Just so we can see these for now, we'll add in a template and a level 1 heading, and the title for this page was create. We can copy this, paste this into the other three pages. This one was category, save this, and the AppHome, and also the Admin too. We'll also use the router to switch between all four of these vues. So head out to the routers index page, I'm going to replace all four of these pages with our new pages. The first one is for the admin, the second one is the AppHome, the third one is for create, and then also the category too. The entry points of the home router is going to be this AppHome. We can amend this, we don't need the about page, so we can change this to be the category, the name of category too, and then replace this dynamic import with a regular component. We also don't need the account section so we can replace this to be /create, the name of create, the component. We can also remove the children array. We also need to create one more object, and this one is for our admin route. Over to our main app.vue file, inside here, we're going to remove all of the links, leaving it just in place the router view, so we can grab this div, which is the wrapper, remove this. This app is not going to have a traditional style menu to switch between these pages. Instead, we'll navigate when the user clicks on a category or a particular card. Once the card has been selected, we will then use the router to switch between these card pages. For now, we can test the routes. All we need to do is to go at the end of our URL and test out the category. Also, create, we also have the admin route. This is also fine. Then finally, the homepage. All of these routes are now correctly mapped to our four pages. We also need a header component, so we can place this inside of the components folder. Open this up and we still have this HelloWorld component. We can rename this as the AppHeader.vue. Let's clean this up. I'm going to remove everything for now and place in the template, which is going to contain the header section and also our title inside of a level 1 heading of creative cards. After this, a navigation section, which is going to contain three links to popular categories. So create an unordered list with three list items. The first list item will contain the router link. Pass in the to prop. We don't need to make these dynamics since this is just a regular string of /category. Then we'll navigate to the birthday section. Pass in the text of birthday. We can also copy this list item and use this two more times. The next one is going to be going to the anniversary route, the text, and then paste in the third one at the bottom. This will be Christmas. You can also change these categories if you want to, much easier if we date different section. Whatever route we own, we always going to be displaying this main AppHeader, so we can import this over in the app.vue. In the script section, create our import. We can use the @ symbol to access the source into the components and then the AppHeader. Register this just below, remove the data too. We also don't need this from the previous videos, so we can remove this. Then up to the template, we can register this just above the router view, so this AppHeader will always be displayed at the top. Then the content from our router view will be placed just below. Good. So we now see the header, we can test out these links. We have the birthday, the anniversary, Christmas, but this header needs a little styling. You can place all of the styles into the header component if you wanted to, but I'm going to place in the general list styles into our app.vue. Also, the section will not have the scope attribute, so this will apply to all of the list items in our app. Removing navigation styles, these were added in automatically from the router, access the unordered list. Then inside here, we'll just reset any browser defaults by setting the margin to be zero and also the same for the padding too. After this, I will list items and also our links. We set the text decoration to be none. This will remove all of the underlines from our links. The list style to also be none, and to also remove the default link color, we'll set the color to inherit from the parent. Okay. These are just general resets and defaults for our list items, and for the more specific header styles, we'll go back over to our AppHeader and then create our style section. This can also be scoped. First of all, all of this content was wrapped inside of this header. I'm going to set the font family by using a Google font, which is inside of our index page. From the starter files inside of our index page, this is linked just here. This gives us access to two font styles which we can use in our project. You can also go over to Google Fonts, so any different font provider, and use a different style if you prefer. Back to our header section, a cursive fall back, and then also the letter spacing of one pixel. This will just add a little bit more space in between each one of our letters. The font size of 18 pixels, a border on the bottom of one pixel, a solid line, and the color of light gray, and pixels all padding also on the bottom which is going to separate the content from this line. Then we can access the flex box to move this title over to the left and then our links over to the right. Display flex, space between would justify contents and vertical alignment with align items by placing these into the center. Very soon, this level 1 heading is also going to link to the home route, so we can add a cursor to our h1. The cursor of pointer, and also remove any default margins or the heading. Over to our links over on the right, grab the unordered list, and then we can set the alignments with the display flex. Since the default flex direction is row when using the flex box, these are now placed across the page. We just need some space in between our list items. We can do this with some padding on the left of 0.5 rems, and now, our header is all complete.
87. Programmatic Navigation: These router links which we have been using so far in our project allow us to change to different locations at the click of a button. Earlier, we also briefly looked at accessing the routers instance using dollar symbol router. This gave us access to things such as the parameters in our code. This dollar symbol router also exposes many of the things to suggest the ability to navigate using methods such as push. This means we can also build in navigation too into our click listeners or methods. Over in this AppHeader.vue, we have this site title in the level on heading and really what we would want is to link back to the homepage and we could either wrap this in the router link or navigate programmatically by accessing the dollar symbol router when we click on this. Let's take a look at how we can do this with the routers push method. Inside of the open inside we'll listen out for a click as we would normally do, and then we'll respond to this by accessing the router which has this push method. Inside here we can add a path which we want to navigate to, so add the forward slash, click on our site title and this now navigates back to our homepage. This is also useful for things such as pushing the user to the account area after they've logged in since this can also be used inside of a method too, the only difference is if we were to use this down in our script section, remember, we would need to add this keyword in front. Let's take a look at something else which is the replace method. Refresh now to the birthday and then our title is to be is to work exactly the same, but there is one key difference. When navigating in the browser, each page we visit is added to the previous history of entries, this is how the browser knows where to navigate to when we click on these back and forward buttons. When we use the push method which we just looked up before, these entries are also added to our history. However, as it sounds when we use the replace method, just like we are now, this will then replace the current entry rather than add a new one. We can check this if we refresh, we'll go to birthday, then click on the home route and now if we click on the back button, we're now take him back to the previous link, which is birthday, because this is being replaced. If we go back to the push method and do exactly the same, refresh, go to the birthday link, back to our home and now if click on back this then takes us back to our original birthday link since the forward slash is being added to the browser stack rather than replacing the previous one. Alongside this router also has a go method which will allow us to navigate through the browser's entries, so rather than navigate into a certain path just like this, we can cycle back or forward through as many entries as you want to. If we wanted to go back to pages, we could use negative 2 and let's try this out. Go home, click on birthday, anniversary and Christmas, then click on the site title and we're taken back to entries which was back to our birthday link. If we change this to be a positive 2, click on this, this will then push us back forward to entries, which is back to our Christmas link. This is useful if we wanted to go back and forward multiple pages, however though, if we only wanted to go backwards or forwards for one-page, we could also instead replace this with forward or also back. This will work exactly the same as go but will only work for one single page. As well as these, there is also some other methods too which you can find inside of the API docs if needed, but these are some of the more common ones. Our project, we're going to leave this as push and link this to the string or forward slash, which we'll link to the homepage. Next we'll take a look at the different types of history modes which the router provides.
88. Different Router Modes: In this project, we're using a starter project which is downloaded from GitHub. It already has the Vue Router installed. But when going through the setup process for the router, we are asked if we wanted to use the history mode. Its history mode is not included with this project's starter, so we are currently in what is called the hash history mode. This is why we have this hash inside of the URL. Since we're using a single-page application, this hash is just used internally. The part after the hash is the route which you want to navigate to. Here we are navigating /category and then our selected category. Having this hash inside of the URL however, can have a negative impact on SEO. If this is a concern, we can change the router to make use of HTML5 mode. To do this, we'll make this change over in the router's index file and then scroll down to the bottom. Instead of the web hash history, we can replace this with createWebHistory. We also need to import this from the router package. Up to the very top, we can replace the hash version with the web history. This and onto our project, we can remove everything after our localhost by the navigation. This all works exactly the same, but this time without the hash in the navigation. This works fine here. But remember that our application is a single-page up, and all of the routing is handled on the front end. This, when deploying our application, we must set up the server to always return our main index.html page. Having this set up will allow our router to handle where we need to be on the front-end if the user visits the wrong page or a dynamic route. We'll take a look at how to set this up later when we deploy our application. Additionally, since we're only getting back the main index.html page, we also need to handle any 404 errors too. We're currently doing this inside of our router, that is catch-all route. We're going to test if this is working by going to any unrecognized links. We still see the "Page not found". Also, if we click on any of our links at the top, we get the "Page not found" component since we have not setup the second section just after our category. Currently, what we only have is /category, so this is an unrecognized route. But if we remove the last section, we now have a match of the category component.
89. Section Intro- Composition API: Welcome, you've made it to the composition API section. This composition API is a view version-free feature and something which is probably the most eagerly awaited part of this new library. We'll take a look at composition from the beginning using sets of functions, we'll also integrate it into our creative cards application. We'll look at all of the new features which provides how we can split up our code into composable files, how we can import and reuse it. Also, we'll look at how we can integrate some of the things we already know, such as: different methods, how we can create functions, how we can create computed properties, watchers, and also some of the new features such as watch effects, and how to add reactivity too. I'm looking forward to this section and I will see you in the next video.
90. What Is The Composition API?: When the Composition API was first announced, it initially received a bit of negative feedback in the Vue community. This was mainly due to a lack of understanding of what the Composition API was intended to achieve. We thought that the vue.js core team was intending on replacing the existing Options API which includes things like the data, the methods on the computed sections, and replacing it with a whole new thing to learn. This is not the case, however, the way we write vue.js code with the Options API is still completely valid, and everything we've covered so far is essential in learning vue.js. With Vue 3, though, the composition API is also provided as an additional way of writing Vue code. A lower composition is used in place of the existing Options API. The Vue team recommends only using the Composition API when your existing project is becoming large, hard to maintain, and difficult to navigate as a developer. In fact, both of them can be used together in the same components if needed, making refactoring much smoother. The motivation behind composition was to make it much easier to organize, maintain, and reuse our code. Reusable components take us so far, but the actual JavaScript inside of these components can be organized in a better way. It will have a typical Vue component which has a user, products, and a shopping basket. The user related code is in the data section, mounted, there is a method, and also a watch too. We also have products in data, we fetch them using a method which is called when the application mounts, and the remaining code is for our basket. The user can update or add products to the basket, they can then check out with the contents of this basket, and we also have a computed section to only show the basket if the user has added an item. This is traditional vue.js code and nothing at all wrong with this. Why introduce a new way? As mentioned before, this is fine until our application gets much bigger. This is a pretty simple example where we can see a high-level overview of all of our code, it also has been simplified to fill this page. We have no code inside of our methods, and in a real application, this simple example could easily be 3-4 times longer. Another drawback is our related code is all separated. All of these related parts are not glued together and it only becomes more of a problem as our files get bigger. This is one of the things composition aims to resolve. We can now write code more like traditional JavaScript and also group our code much more easily. In regular JavaScript, we may write the user section like this, using a constant [inaudible] the use object, and a function to get the user from a database. For the products, we could also have the product array and also a function to get the data which we need. Also, for the basket related go to, maybe some code which overlaps down the line, but we can already see that grouping these together to make our code much more organized. But where do we put all this code? As always, it goes in our export default section. Both of this, we wrap it in a setup function inside of here, we then also return anything we want to then be available to use in our template such as our constants and our functions. We're not just leaving Vue behind here either with this new look syntax. We still have access to familiar things such as life-cycle hooks, using the on prefix. Here, we see onMounted to get the user, and also the products when the component mount. We also import this from the Vue library as a named import, meaning we only import the parts which we need. Also, familiar things like watches and computed is also available too. In this example here, we import computed and wrap our show basket code with it to keep it updated with any changes. The second thing composition aims to improve on is reusability. This refactor we see has not made the components smaller or more reusable, it simply just grouped together the related sections. We may want to reuse the use objects and also the products too in a different part of our app. Current solutions include the possibility of getting this data at a top level component and then passing it down as prop. Although this can get messy when components are deeply nested, it can also place coding components which it was never intended to be in. We could also use a state management libraries such as Vuex which is a good option and provides a central store. Although this is something else to learn and also some extra way to add to our project, would it be great though if Vue natively provided a solution to make our code more easily accessible and reusable? Thanks to composition, this is now possible by moving our reusable code into separate files. The folder name is obtuse, but you may commonly see it called composables where each section of our code is then placed into a JavaScript file, commonly using the use prefix, which we can then import into any components which needs to use it. This leaves our components much lighter and more reusable. All of this code may look strange and confusing at first, especially if you are new to Vue. At first, it did to me too, but we are going to be spending a lot of time writing composition code in the upcoming sections, so you will get lots of practice and this will soon become more familiar.
91. Composition Setup: In the previous slides, we looked at how our composition code goes inside of a setup function. Let's now set this up in our AppHome.vue, and here, we need a script with our export default. Then we'll pass it in our setup function, which looks just like this. Setup is executed before the component is created. It's inside here where we'll write our composition code in a more JavaScript like syntax. As we've just seen in the previous video, this homepage will be the entry point for our user. We'll use it to display a list of all of the available categories for our cards, the child's birthday and anniversary. In the supplied data.js file, let's open this up, just here, each one of our cards has a category section which includes an array. It's this data which we're going to loop through for each card to construct this page. So we will need to loop through all of our cards and then we'll create a second loop to loop through all of the categories and then save these. First of all, let's import this data which we need in this component. Just below the script, we'll import the cards array. The file path for this was../data. We would then normally set the cards to be a data property. But with composition, we can create a regular JavaScript constants or variable. So let's set up a constant called allCards. We can initially set up an empty array. Also, we need a second variable which we're going to to use to store all of the categories we find. We will loop through all of our cards. So this is going to be pushed too. We'll create a let and we'll call this uniqueCategories. We could set this up to be an initial value of an empty array, or even better, we can replace this with a new set. A set is a collection of values, and just like an array, we can loop over them too. But the difference is it will only accept unique values. This is ideals in some of our cards have the same category, so we don't want this data duplicated. Also, a set object is regular JavaScript and nothing related to vue JS. Next, we can create a function called getUniqueCategories. More functions in composition, they can also be wrote in any other valid JavaScript type too such as arrow functions, if you prefer. As this function sounds, this is going to be responsible for assigning all of our categories to this variable. So to begin, we'll grab all cards. In fact, we just need to assign this to cards rather than our array, and then we can loop over these with forEach as an a function and each item will store this in the variable called card. So remember, each card inside of our data file had these categories property. So we'll create a second loop where we select our card and then the categories, and then a second loop with forEach, which takes in the category. Then we want to push this category to uniqueCategories. Now, if uniqueCategories was an array, we could use the JavaScript push method. But since we set this up as a set, we need to use the add method passing in the category. Then outside of our function, we can log this console to test this out. This now leaves us with our function, but now, we actually need to call this the code to be executed. So to do this, we can add this to wait onMounted lifecycle hook where we're passing it the function name. When using the lifecycle hook, so you have the same name as we've previously used such as mounted, but for composition, we add the on prefix. Also, when using any of these features like this two, we now need to import them individually from the vue library. So we can import, the named import the onMounted lifecycle hook from the vue library. Let's give this a try and open up the browser developer tools into the console. Here, we see our set with 11 values. Let's open this up, and we'll see 11 unique values. So this is all good. We now have what we need, but it would also be useful to have a category for all cards too, and we can also add this into. So at the top of our function, our loop will also access our uniqueCategories, call the add method, then we'll add the all category, refresh, we now have 12 values, which also includes the all category too. Finally, we can return back from set up any values which we now want to make available to use in our template. This will be a object, and the only thing we need to return to our template is uniqueCategories. So add this in, move to our template, we can remove our level 1 heading and instead place in an unordered list where we're going to loop through all of these categories. So the class of grid wrapper, we'll add this soon for our CSS, the list item, which will have the class of category item. We can also pass in v-for to loop through all of our items. So we're going to loop through category in categories as in a key. Since we use set, each one of our categories is unique. So we can also use this as a key. In between the list item, we can create this into a link by passing in the router link. This takes in the to prompt and passing all the object where the name of the link is going to be category, which we set up over in our routers index file, we'll take look, this is just here. In fact, we need to use a uppercase C for this name. Also, pass in our parameters object to where the category is going to be equal to or category from our v-for loop. So this will now link to forward slash category and then forward slash the name of our unique category too. This link also needs some text in between the router link component. So passing our category in between the double curly braces. In fact, this just wants to be unique categories. It's much the name which we returned back from composition. Since we're now passing parameters to this route, we also need to go over the routers index file and also add a dynamic segment called category. So now, if we refresh the browser, we don't see anything on the screen. Now, this seems strange because we know we have the categories because we logged them to the console just here and we've seen 12 unique values. Well, the reason this is happening is because of the onMounted hook. At the start of this video, we mentioned that the setup is ran before the component is created. But onMounted is called after this. This means we are initially passing uniqueCategories just here as a empty set. Initially, this is the value which is being returned back to our template. So we are looping through an empty value. This basically means we need to call our getUniqueCategories function before this mounted phase. One option is to remove the onMounted hook and to call this just like a regular JavaScript function. Since we're no longer using onMounted, we can also comment this out and also the browser, refresh this, we now see our uniqueCategories. If we click on any of these, we see our category component, and then we take it to the correct URL. Or alternatively, if we didn't need to call this function at any time, we can also move the function wrapper and just leave the code to run inside of setup. Save this, and over to our homepage, this also works exactly the same too. The ability to add in code like this to run on setup means with composition, we no longer need the before create and also the created lifecycle hooks. Both of these have now been removed in favor of this approach.
92. Primitive vs Reference Types: To help you better understand reactivity, we're going to first look at the difference between primitive and reference types in JavaScript. If you are already comfortable with the difference, you can move on to the next video. If not, this video will help you prepare for the upcoming videos. You don't need to type this out or follow along with this coding, but there is something I want to show you to help you better understand what we are going to look out, and to see this, we're going to go over to the app.vue and then down to our scripts. What I'm about show you is passing a simple object as a prop. Inside of this app.vue, we're going to create a data section. This will look pretty similar to what we had in the early videos. We're just going to return a new object with a user. This user will create a name property and also, a user ID. Up to the template, the only component we have to pass props down to is this app header, so let's pass our props down to this. The user is equal to the user. This, and then we can go over to this app header components and we will typically do something like this. We will create a script section. Setup the props. If as an object or an array. We'll keep this simple and just pass this in as an array. We'll then maybe accept these props inside of the data section, where we could set a date property such as the use object to be equal to our prop, which is this.user. Now, this receives our user prop and stores this locally in the components so we could then modify this, maybe using a form with V-model. Let's do this just anywhere in our templates. Setup a text-based input. We could set up V-model, linked to our user object. Let's go for the name. Do this and we see our input and our name as expected, but if we go back to our app.vue where we have this original data, let's quickly output this. You need double curly braces placed in the user. Now we have our data in two locations. We have the original data from our app.vue. Then up here, we have the props which has been copied inside of this app header. Let's give this a go. We can modify our input. Let's add something here inside of here. We see as soon as we update this app header, that also, the data inside the parent app.vue is also updated too. Now, this may seem a little strange because we've passed this down as props, we've then taken a copy, and we should now be only modifying the data in our app header. Normally, we would expect this behavior to only happen if we emitted a custom event to change the data inside of the parents. But this is doing it automatically. Let's take a look at another example back in the app.vue. The use the object will pass down a simple username, which is just a simple string, and now open the app header, we can also pass this down as props. Username to be equal to username, and also output this inside of our app. Over to the header. Pass this into our array. We'll do the same as our object, pass this into our data. The usernameProp is going to be equal to this.username. Let's also duplicate the input, the type of text, and this time, we'll V-model to the usernameProp. Now, if we try to modify this username, if not anything, this time, there's no changes in the parent components. Yet if we do this to our original object, we see the parent components will update. This may seem strange at first, but it's not a new JS feature. It's something related to JavaScript and data types, which can be placed into two groups and these groups are referred to as primitive and object types. Object types in JavaScript are considered to be functions, arrays, and an object itself. Primitives are data which is not an object and types of string, Boolean, number, undefined, symbol, and BigInt. The part we're interested in is inside of this app header, you will have all two props of user, and username and then we take enough copies of both of these values. The difference is both of these values fall into our two groups. This first one is an object reference and the second one is a primitive. This current one is a type of string and this is where the difference lies. When we copy a primitive value, just as we do, we'd copy in this username, each copy holds its own unique value. You can think of this as two separate values, which is why when we update our input and our username, the original is unaffected. Object types, however, like we have with our user, will behave differently. Even when we take a copy of an object over the objects that are pointing to the same value in memory, even though they are in different components and locations. This is also known as a reference types since all variables are referring to the same thing and it's this reason why we see the original name updated. This leaves us with a question, why use things such as emit to provide updates in the parent components? Why not just always pass object types? After all, this would save a lot of code. This is a reasonable question and there is nothing stopping you from doing this if you wanted to, especially on a smaller, easier to manage project. However, it's generally recommended to avoid this if possible, since it may begin to get confusing if multiple sources are mutating the same object. An option is to make a deep clone of the original and still use the techniques such as emit to only modify the data inside of the parent component. We could also flatten or reorganize our data to use as objects or we could restructure our props to only pass down object properties rather than the full object. Another option is to also only pass down the object as an initial value to the child, and then modify a different date property on the parent. Whichever option we choose, the key is to make updates more predictable and to ensure we know what components or what methods changed our data at any given time, rather than guessing which reference to the data caused the updates. If this is all completely new, it may seem a little confusing, but the key thing to remember is a primitive, just like a simple string or number, is more like an independent copy. But an object type just like this will still point to the original object. Now, we don't need any of this in our project, this is just for our demonstration, so we can remove all of these, include inputs in app.vue We don't need these props and we can remove our data properties too. This understanding of object references and primitives will become more useful in upcoming videos, where we will look at making our composition data reactive.
93. Quick Introduction To Proxies: The free reactivity is created by using an ES6 feature called proxies. Vue 2 use an ES5 feature called Object.defineProperty, which created the getters and setters to update our data. These handsome caveats, one of these was being of a new data property was added after the application loaded, it didn't automatically become reactive. We could change existing data properties, but not add new ones dynamically. This is being resolved with proxies. A proxy is like a wrapper for an object. This wrapper allows us to intercept it and make changes before performing an update. Proxies can be a complex subject, but I'm going to give you a quick overview so you have some basic understanding. In this App.vue components, let's add a simple example to our script. To intercept an object and make some changes, we first need an object to work with. Outside of our export default, I want to create a constant called state, and set this up to be an object, passing a user property and also a occupation. Then we can set up a proxy with new Proxy, capital P, which will take in the targets and the handler object. This may constant called proxy. This target we see here is the original object we want to proxy. We can name this to be state, just like our object just above, this is the target which we want to intercept, then a handler which is an object to set-up or we want to do to this original target object. Let's also set this up too, cons handler, and so this would be an object. After this creates a console log, I'm again, going to output the contents while proxy. Remember, that's our proxy point to the original object which you pass in just here. This is our state. This now gives us access to all of these properties such as the user and also the occupation. First, let's try the user, go with the browser, we can go into the console, open this up. Now, we see the username of Chris, the occupation. There we go. As you may expect here, we see the original user on the original occupation. Since this handler is not setup to create any modifications just yet, its handler does have a number of functions which we can use. These are sometimes called props. One of these functions is get. This is used to get one of our property values. Pass in the targets and the prop, and then we can do some console logs to see exactly what data we have for each one of these. Go for the target. Then this one is for the prop. Save this and refresh the console. First of all, we see the targets, and if we open this up, we see the user and also the occupation. Since this target is the original object, after this, we're going to see the prop, which is the occupation, and this prop is the individual property which we are selecting, in our case, we are selecting the occupation. This will point to all occupation inside the console. The target is the overall state object. Then the prop is the individual property from this object. Now, we have access to all of this information. We can now intercept these values and do whatever we want to them. For example, we could return the original prop, which is toUppercase. Save and refresh. We see the occupation which is now uppercase. This is how a proxy works. It takes the target or the original object, and then it will intercept this object and grab any of the values. We can do whatever we want to these values. Alongside gets, there is also a handler functions too, such a set, which is used to set the new property values. Everything we see here is just a regular JavaScript. How does all this work with a Vue.js? Well, anything we add to a data section is automatically converted to proxies. Anything which we add in a data section inside here will be automatically converted to be a proxy of the original object. Just like this get handler just here, Vue will also add its own handler function, that is proxy, which has getters and setters to enable Vue.js to track and update the data each time there is a change. This is how it works for all data properties. But what about the regular variables we create with composition? Well, for this use case, Vue also provides some rappers to add reactivity. We'll take a look at these in the upcoming videos.
94. Using Reactive & isReactive: To kick things off for this video over in the app.vue file, we can remove all of the proxy demo from the last video. Remove everything up to this date object. Now we're going to take a look at how we can add reactivity to the data in our composition API. We've data properties we've been using so far on the options object. By default, everything is reactive. Meaning if we make any changes to this data, any other parts of our application which relies on that data will also be updated to in V3. As mentioned in the last video. This is because VGS will convert these properties into proxies when using composition. Now these variables we declare are not automatically reactive, just like when we create variables with regular JavaScript. These are the ones which you've added inside the setup function inside of your app home. Just like the unique categories and all cards. You free is much more modular than V version 2. We first need to import the reactivity package, which we need to from the view library. This means we only import the part which we are actually using in our application. Let's see how this works in composition. Inside of this app home and also inside of the setup function. We can create a JavaScript object to hold some data properties. Let's call this data, and set this equal to an object. This name of data is completely different and is not related to a regular dates property. Inside here we can add some properties to this and we'll go for name and also a Occupation. Now at the minute we don't have any thing to update this data object. To simulate an update and check this out, we're going to create a setTimeout function at just below. We want this function to run every three seconds, so you can pass in 3,000 milliseconds. Then we'll want to do to access the data object, and then it changed the data inside, such as the occupation, or access the data, the occupation. We can set this equal to retired. You check if this is working. We can return our data objects back from our composition. This now means if we scroll up to the template, we can now use this inside of our code. Since this is an example, we can place this anywhere. That's access our data, Dots occupation, Refresh the browser. We see the text of a web developer. If we wait for three seconds, we don't see any updates to this code. Even though we should be changing this to retired after three seconds. Even though we are changing this after three seconds, it just with regular JavaScript, the user interface will not be updated. For this, we need tell VGS that we want this data to be reactive. This first technique which we're going to take a look at to do this is by importing, the reactive package. First, as I mentioned before, view is modular, so we need to import this from the view library. This is called reactive. We can then use this reactive package as a wrapper for all of our data. We'll cutout this object out in reactive. Then inside we can paste in our object, refresh browser. Now after three seconds, our user interface is now updated with this new data by using this reactive wrapper. Any data which is inside which changes or is updated, it will then notify any other parts of the application which relies on this. In our case, the Occupation. This happens because view will take a reactive copy of this original object and then it wrap it in A proxy. Reactive is also used behind the scenes to convert objects when we use the data properties with the options API. If you wanted to perform a check to see if an object was reactive. We also have access to Is reactive to. This can also be imported from the view library. Is reactive can now be used to check if an object is a regular, JavaScript object, or if it's a reactive proxy. You see this one creates a console log, access is reactive. Then we can pass in our data, open up the developer tools into the console. We can see this is true. However, though inside of our setup, we also have access to our cards which use a regular object. Instead, if we place in all cards, refresh, we now see the result of false since all cards is a regular JavaScript object rather than a reactive proxy. This is how we can use reactive. View also provides a never reactivity feature called Raf, which we'll now take a look at.
95. Adding Reactivity With Ref: Reactive objects, just like this one, are ideal if we have multiple properties, just like we do here, especially if they are also related to and we want to group them together. But what about simpler values like strings or numbers? If we just had a single value like a primitive, let's say a user, which was equal to a string, we could also place our user property onto an object, just like this, and then use reactive, or for single values like this, it may be more convenient to use a ref. Now, to see a ref in action, we also need to import this from the view library, and then just like reactive above, we also use ref as a wrapper, so when we can cut this out and wrap this inside of our ref. To use this in our template, we also need to return this from setup, and let's add this to our template, and set our user to be equal to this user which we have just returned. Okay. So we now see our user inside the browser, but we also need to test for reactivity too. We can do this inside of our timeout. Instead of updating our data, we can now update the username. So this and refresh the browser. We see an error that we're trying to update the user, but this is a constant. So this is fair enough. We can change this to be let and see what happens, and after three seconds, we don't see any updates to this username. Now, this may seem strange, but there is a reason why this is happening. Behind the scenes, our user value, which we have here, is also being converted to a reactive object, and to access our object's value, it gives us a property called value, and we need to reference this every time we want to access our user. So to access the name inside of here, we need to access user.value. Save and refresh the browser. Three seconds later, our user is now updated. This value property always needs to be used when working inside of the JavaScript section. However, up in the template, just here, as we can see, we don't need to use it inside the template. When accessing a ref value inside the template, the value is automatically unwrapped for us. Now, with ref and reactive, this now leaves us with two options when it comes to making values reactive, and either way, we end up with a reactive object. The exact use for either is pretty flexible, both as a general rule. I use reactive when I want to group together multiple properties or values in a object like syntax, and then use ref, just like this, and we'll a single standalone value such as a string, a number, or even a single object too. But again, the use case of both of these is pretty flexible and it's up to us as a developer which one we prefer to use in our project.
96. Destructuring & toRefs: We have reactive objects just like we have here. Sometimes you may want to pull out a single value from this object. For example, if I had an email, well, this use case and especially if we need to access multiple values, we may often make use of JavaScript destructuring. Just like this, we could setup a constant called email and extract this from our data object. This will now extract the email property and save this to the email variable. We could also access multiple values if we also wanted the name just like this, we could access the email and also these name variables too. This is pretty common and this is just regular JavaScript. But let's see what happens when we try to render this out to our template. First, we need to return this from our object, and up to our template. First, let's change this to be our full data object, and then after this, our email. Again, we can scroll down to our set timeout and simulate an update by changing our data. What we're going to do here is access our data object and update the email property. It can just be any string. Over to the browser and refresh. Three seconds later, our original data object which we have here, is now been updated. However though, our copy which is stored inside of this email property is still the original value. This happens because when we destructure values out of an object like this, it will lose all of its reactivity. Meaning any updates which we make to the original object will not be passed down to this variable. If we still wanted to make use of the structuring like this, we'll still hold on to the reactivity. We can wrap this data object with something called toRefs. Open the import, let's import this, and then use this as a wrapper to our data. Let's try this out, save this, and refresh the browser. After three seconds, our email on the data object is updated, and also the copy too. What we're doing here with toRefs is effectively converting all of these properties on our object to make use of ref. This will mean we can keep the reactivity so if any of this data changes, our copy will also be updated too.
97. Readonly: Sometimes we may need to wait to control exactly what components can update our data. If we have a reactive object which was shared across multiple components, we may want them to all read the data or only allow some of them to update it. For this, we have available readonly, which takes an object. This can be a regular JavaScript object or a reactive one, which uses either ref or reactive, and then returns a readonly proxy to the original. Again, we need to import this from the vue library. In the AppHome, in our import statement, pass in readonly. Then what we're going to do is to take a readonly copy of our data, store this in a constant called readOnlyData. Then just like we've done with reactive and with isReactive, we then use this as a wrapper, passing readonly, and then we want to wrap our data from above. We now have this new data, which we can return from our setup and then we can output this loop at the very top. Let's clean this up. First of all, we'll type in readonly, and then we'll output our readonly object on the e-mail property. Also the user, so we can test the original object, is still working. We can output the data.email. Down to our setTimeout where we simulate an update. Inside of here we're still updating the original reactive object and certainly new e-mail address. So let's go to the browser and refresh and see what happens. After three seconds, both the readonly version and the original has been updated. So this means that the copy is still reactive. Let's see what happens if we try to update the copy in the Timeout rather than the data. We'll change this to be readOnlyData. Save this and refresh. Three seconds later, nothing happens. This update will now fail because the copy is readonly, and this allows us to protect any data which we don't want to change in our components, or data which you may want to control more closely.
98. Computed: Computed properties are also available in composition too and we can now make use of them to filter out our cards by the selected category. Over in the AppHome.vue components, up in the template, we have a link which links to our category and then forward slash our params. In fact, this can be shortened and see both the same name and also over in our router's index page. We've already setup a path to handle this, and then display our Category component. It's now inside of this Category where we can now handle, filtering out these cards by the clicked on category. Just below the template, we'll create a script section, and then we'll create a setup section which is going to handle all of our composition code. We need to access all of the available cards to be able to filter. We can also make this reactive too by importing ref. First, we'll import all of our cards and we'll import these from dot dot slash and into our data. Since this will be reactive, we'll also import the ref from the vue library down to the setup. We can now import our cards into vue JS, store this inside of a constant called allCards, as in the ref as a wrapper, and we'll wrap our cards. Just as a temporary measure, we're going to set up a constant called category. This is going to store in the category which use the clicks on such as travel, Halloween, or birthday. But we will soon get this from the URL parameters. Now, we'll set this to be all. Then we can setup a computed property which watches this category and when it changes, it will then return the filtered down cards. We set this up as a variable or a constant and once I call my in cardsByCategory, and then we can assign this to be a computed value. Inside here we pass in a function which will run each time the data changes. Since we are now using computed, this also needs to be imported from the vue library. Now, inside here we can return one of two values. First of all, if the category is equal to all, all we want to do is to return all of the available cards. We don't want to perform any filtering. We'll first check if category is equal to all. If it is, we'll then return back the data, which is the allCards. Since this is a reactive reference, we also need to return the value. If this is not equal to all, this means we've clicked on a category and have some filtering we need to do. Instead, we'll return allCards.value, call the JavaScript filter method. This will then run through all of our cards and then we'll run a function for each one. This will only return back the filtered version of all cards. Now, we need to pass in the individual card and perform a test for each one. The test which we're going to add in is to run through the card, select the categories, and then we want to check if the category includes a certain value. The category which we want to check against is this category just here. Just to recap, we're looping through all of our cards and then we're going to return a filtered version. The way we filter is by selecting each individual card looping through the categories and checking if it includes the selected category from above. Just before our closing, set a brace. We'll then return this. We can use this in our template, up to the top. Inside of the Category header, what we're going to do is select the router or the current route. Then when we click on any of these, we can then take into forward slash category and then we can access the individual params. Select the params and the one we're interested in is the category. Make sure this is spelled correctly. See anniversary, husband, wife. Then below this we can check our computer property is working by outputting the cardsByCategory. First of all, we'll click on any of these links, we'll see a list of all of our cards. Now, we don't see the filtered version just here because we've set the category to be all. We change this to be any other value such as birthday. We should now only see cards which have been filtered down by the birthday category. We can try one more. It's by wife, refresh, and we can see the category for card number 1. Includes the category of wife. Now, we know this is working. Let's now go over to the template. I'll put this in an unordered list, a class for the CSS which is going to be grid and this is called wrapper. Then a list item which will include the for loop, which will be card in cardsByCategory, a key. Each one of our cards has access to a unique ID. This is an ideal key to use. Between this list item, we can also allow the user to click on this card and then we forward it to the edit screen. This will need a router link. Now, we'll keep it simple and we'll output the card.name. It also needs the to property. We'll just link this to the homepage. Save over to the browser, click on any of these, and then we're taking it back to the homepage. Now, this all works and a bit later on we'll come back to this and we will link these cards to the edit screen.
99. Standalone Composition Functions: Inside of this category.view file, this composition code we have wrote so far only solves one of the concerns, which is we can now group together related code. For example, we have this category variable, and we have this organized and placed next to our computed section, which relies on this variable. With options API for example, this category would be a data property. Then we may have this computed section a lot further down in our component. Another benefit the composition aims to solve is the reusability and organization. Even though we've not yet wrote a lot of code for this project, we already seen some duplicates. For example, here in the category we're importing cards and then assigning them to this constant. Over in the app home we are importing the cards and then a bit further down, we're doing exactly the same just here. To help with this, we can extract our composition code into standalone functions. These can also be placed into their own separate files. To see this would go over to the sidebar, into the source, well we'll create a new folder called composables. This folder name is totally up to us, but it is common to save this named composables. Then inside we're going to create a new JavaScript file. We want the file name to be descriptive, and again, the name is totally up to us. But the common naming convention so far seems to be to have the use prefix, so we will have names for our projects such as useCards and also useCategories. This one will be for useCards, and this is a regular JavaScript files, so we use the.js extension. Inside of this file, we're going to export default function. I want to give the same name as our file, which is useCards. This now means we can extract all of the composition code from any of our setup sections and place it into this function. We can then import it and have access to all of the data and all the functions which we place inside. For now we're going to select allCards, let's copy this and then paste this into our function. We also need to have access to our data and also import the ref from the vue library too. Just like here, we'll bring over these two import places outside of the function and we don't need computed. We're not going to bring over any of the category related sections because these are going to go into their own separate composable. All this file is responsible for doing is to fetch the initial cards from our data file, and then just like when we use this in our components, we're going to return an object. This is going to give our components access to any of the data, any of the variables, any of the methods which we have in this function. This is pretty simple, all we have is allCards, so we can return this. At the moment this file is not actually doing anything and it's just sitting there and we can import it when we need it. But next, let's create a new file, which is for the code related to our category. Therefore, we'll give this a descriptive name called useCategory, with the js extension. Set up our export default with the same name as the file. This also needs to return something too. To begin over in the app home, this component is getting a list of all of our categories that just here. Then over in the category we're getting the individual cards for this selected category. It would make sense to extract all of this category related code from both of these files and then place it into our new composable. I head over to the useCategory and to begin, we are going to need access to all of our cards which are inside of this useCards. Remember we've exported this and return this back from our object. Now we can import this into our composable, which was useCards. The file path is./useCards, and this is one of the good things about composables. We can reuse data between each one of our files. Here we have effectively imported the full function from useCards. Now we want to pull out the allCards variable. Now we're going to store this in a constant, this is allCards, and then we're going to extract this from our useCards function. We can now start to bring over all of our category related code unless we going in the app home. We already have our allCards sections, so grab the uniqueCategories and also the loop just below. We can cut this out then place it just below allCards. Then we also need to return a uniqueCategories. With this now extracted into its own file, we can go back over to the app home or any other components which needs this information and import this to use. Goodness app home has quite a bit of example code, so we can remove all this from the setup function. We don't need allCards. I'm going to remove our return statements for now. We don't need the data, so we remove all of these imports. The import we do need now though is our useCategory. This is importing our useCategory function, and the file path was the source, which is the at symbol. We'll head into the composable folder and then into useCategory, and make sure this is spelled correctly, useCategory. This useCategory function is exporting our uniqueCategories, and now we can go into our setup section and install inside of a constant. That's extract uniqueCategories from this useCategory function. Make this available in our template, we also need to return this and open our template. Remember, we'll make use of this uniqueCategories inside of this v-for loop. Let's try this out, refresh, now we don't currently see anything inside the browser, so let's take a look inside the developer tools into the console. We see that allCards.forEach is not a function. Now this is happening in our useCategory.js file. Here we're trying to loop over allCards, and this is thrown in arrow because when we first wrote the section, allCards constant was not making use of refs. But here inside of allCards. If we go over to the section where this is created, we open this in a ref, this now means we need to access allCards.value, and this should now fix the problem. We still see an arrow cannot read property, e-mail and this is probably from the template. Yet we can also remove this, just try ones more, and there's all of our categories now back on the screen. But this time from a composition function.
100. Mini Challenge: Moving Over The Cards By Category: Although all of this new code may seem like a lot to take in, but as with most things, it just takes a little practice. This was also confusing to me at first too when I started learning all of this new composition code. But by the end of this project, we'll get lots more practice starting right now, where we're also going to move over the code from this category.vue. What I would like you to do is looking at this setup section, we're going to remove all of the category-related code and move this over to our useCategory composable. The related code which we need is this constant. We'll also need the cards by category. You will also need to import the computed section and move all this code over, just like we did in the last video with AppHome. Then you need to clean up this component to remove any unused code, and also import our composition file, just like we did in the last video. So give this a go, and if you get stuck, I will run through this in the next video.
101. Moving Over The Cards By Category: Let's now run through moving over the category related code from this category dotVue. If you are confident that yours are working correctly and you completed it successfully in the last video, you can skip this video. To begin, we need this category constants and also our computed section too. Cut this out, go to the useCategory, paste this in. Then we also need to return the cardsByCategory, so I'm going to use this in over files. This is also making use of computed, so we also need to import this, only Vue library. Save this file, and back over to the category, we now need to clean up all of the unused code. We don't need the allCards, we don't need the data, we can also remove our Vue library import and replace this with our useCategory. The @ symbol for the source folder, composables, useCategory down through the setup. All this inside of a constant, and remember over in the useCategory we can now return in our cardsByCategory, so we can import this inside the curly braces. We're importing missed from our function which was useCategory. We're still returning this cardsByCategory and using this up in a template. Let's save this file and try this out. When we click through any of these categories, we see the category heading is changing. Currently, we still only have the same cards for each one, because we've set this as the category in our composable. But this is something which we will address in the next video, where we take a look at how we can access a router inside of these files.
102. Accessing The Router: At the moment, when we click on any of these links just here, we're taken through to the category page, but we still see the same cards for each one. So this children category will still show anniversary one. Go to travel, we'll see the same cards. This is because over in our use category.Js, where you set the category, if it is fixed value, this fixed category means that the values is always the same regardless of the category we click on. In this video though, we are going to replace this and make it more dynamic by accessing it, the URL parameters. So currently, we have this travel parameter. We click on any of these. These will always appear inside of the URL. So what we're going to do now is to grab this parameter and then provide this to our computed section. Where we're going to do this is by accessing the router over in the category.vue. So each time we call caused by category, we'll pass in a parameter to this function, and then we can use this inside of here, instead of this hard-coded value. So let's get to work with passing our router to this function. With the Options API, we could do something like this. We could access this.router.params, and this will work perfect defined with the Options API. When using the composition however, this keyword is not available. So we need to access the router in a different way. For this, the view relative package allows us to import functions from the router package. We can access useRouter fully full router object or useRoute for the current router information. First, we need to import this from the view router package. So the first one was useRoute, and the second one was useRouter, and this is from the vueRouter package. To access both of these in a setup, we'll then install these inside of consonants. So the first one is the router, which is equal to useRouter. This is a function so it needs to call it. The second one is the individual route and most of that is equal to useRoute. These two constants will now work just like earlier when accessed $Router for the full router package and the $Route, which is the current route we're on, therefore, we still have access to all of these same things such as router.push, where we can navigate through a new page. Well, this is where we only need access to the current route since we want to, select the parameters. So we can remove the router and also the import too, and we can check we have all the information we need with console.log. Pass in the route.params. Refresh and open up the developer tools, into the console. Have the birthday, which we see just here, and also the wedding too. So now we can make use of this with the console.log, and we can now pass this to our function when we call it. So the route.params, and we want to access the category parameter. Now, let's head over to use category where we can now pass this into our function. Since we've named this category, we can also remove our category constant from below, and now in place of this computed section, we'll use the category which is passed to this function. Let's test this out. Over to the browser. Let's go for wife. We'll see the one which we had just before. Celebrate. All. Travel. So these are all currently working, but if we click on our free links about the top, click through all of these and our cards stay exactly the same. So currently, these three navigation links don't work, but this is something which we're going to fix very soon.
103. Watch and WatchEffect: We discovered in the last video that when we click on any of these category links, this all works fine and filters down our cards by the selected category. However though, if we click on any of these navigation links, the first one will initially load correctly from the homepage, but if we try to switch between these category links, we don't see any further updates. This happens because of the view router, and the reason is because when we are on the homepage, just like this, and we click through any of these categories, we're switching from the home component to the category component. This change of route or change of component causes our component to re-render, therefore, once again, calling our category function. But when we are already on the category component just like this and we switch between these free nav links, we're staying on this category component, but just switch now the params. When we do something like this, the view routes will not re-render the components because we are still on this category component, and for performance reasons, it won't re-render this components just for a change of parameters. Since the view router will not re-render this components for a change of parameters, we need to now watch out for this manually. We've already looked at a watcher during this course, and they can be used with composition too alongside a new version called watchEffect of in the use category composable. Inside of this function we'll receive new category name from the router parameters, and then inside of our computed section, we send back the cards based on this category, and we pass this category to our file just here. Since this category.vue component is not being re-rendered when we click on these navigation links, what we're going to do is to remove this section just here and use a router directly inside of our composable, this will allow us to then place a watcher on this router or more specifically, onto the parameters which we need and then trigger the updates once there is a change in these parameters. To do this, it works just like we looked at the inside of the components, we first need to import useRoute from the vue.router, and then inside the function we'll save this inside of a constant called route. Since we now accessing this router directly, we can also remove this from our category.vue. I'll remove this, we'll pass it to our function, we can also remove the router import and also the constant too. Since we no longer post on this, we can also remove this from inside of here. But remember this computed section is still expecting a category, so we can now create one locally to replace the one which we have just removed, so const category. This will also be equal to route.parameter.category, but this time we're going to make this reactive by passing this inside of ref, and pull this just above. Since this category is now a ref, we also need to access the dot value and also in the includes section too. Something which we need to note here is this category, it's just going to be signed with the initial value when this loads. We then need to manually watch for any changes to these parameters and then update this value. To prove this, we can now log this to the console just below, so log the category.value, open up the developer tools, refresh the page, so we see the anniversary. As mentioned earlier, if we go to the home page and then click on a category, we see the correct one just in here. What if we switch between these three links at the top, we see the category is not updated. What we now need to do is to watch out for the change to these parameters, obtain the category, and since this is reactive this will then re-run our computed section and then update our components. For this we're going to import watch from the vue-library and then pass this inside of here. When we use the watcher, we're composing in any data which we want to watch, such as a ref or a reactive property. Here if we pass in the category, on this occasion this would be of no use, we know this isn't going to work because as we just checked in the console, this category is not going to be updated when there is a change of parameters. Instead we can replace this with a getter function, which is going to return the value of our router directly, access the route.params.category and then separate it by a comma, we attend a call back function, which is going to run when there is a change. It's inside of this call back function where we want to update the category value with the new router parameters, so category.value is going to be equal to route.params.category. Let's try this out over to the browser, and thanks to our watcher, these navigation links will all now work. This watcher which we have just used is the same as when we looked at earlier with the options API, but there is also another type too, which is called watchEffect. With this we also need to import this. WatchEffect is a little simpler than watch, and it looks like this. Inside of the brackets, we can then pass in any function which we want to run, so just as a demonstration, we could do a console log, look the value of our category. This function inside of here will be run each time the data inside changes. In our case, every time the category's value changes, we should see a new console log, onto the console, right-click and inspect, refresh the page, and now we only birthday route, so we see this run by anniversary, Christmas, and this watchEffect is now re-run for each parameter change. WatchEffect is a little bit like a computed property, we pass in a function which is called once any reactive property inside changes, but the difference is that it can also perform side effects too. So rather than we computed which can only return a value, watchEffect on the other hand can directly update our data or state. There is also some differences between watch and watchEffect too. Notice how when we reload the browser, we see the category immediately. If we go back into the console and refresh, we see a console log has been run immediately even though we haven't clicked on any of these links. This is because watchEffect will both run immediately and also when the data inside changes. However, watch is considered lazy and will only run upon a data change. Another difference is watch will also allow us to access the previous and the next values too, and we pass these into this callback function. If we wanted access them, we could pass them in with variable names then we have access to these inside of our function. We currently don't need access to watchEffect for our projects, so I'm going to remove this, and we'll also need to remove the import too just to recap both the watch and also the watchEffect, we'll re-run when the observed data changes. As we've just seen, watchEffect will also run immediately too even if there is no data change, and we've seen this before with the console log. If we need to access the previous and next values, we can do this by passing them to the watchers callback function and use them just like we did with the options API.
104. Using Alongside The Options API: We already know by now that the composition is not intended to completely replace the options API. But what if you wanted to use both at the same time? Inside of our app home dot view components, we have this setup function. We can still parse in sections alongside our setup function from the options API. Such as if we wanted to access any data, just like we've done in previous projects. It can be beneficial to use both when transitioning or refactoring from the options to the composition as your application grows larger, or even gradually moving an application from view 2 up to view 3. Something we need to watch out for though when doing this is that data is not transferable both ways. [inaudible] we can add a name to both the data and the setup. First of all, a name property for the data and then create a constant inside of the setup. We also need to return this, so we can use this inside the template [inaudible]. Let's now output the value of our data properly. This can go anywhere. Parsing the name which is equal to our data property of name. See this, we need to go over to the home link, that's our name just at the bottom as expected. But what about accessing our composition code inside of our data properties? If we wanted to access his full name inside here, we may be tempted to do something like this. We could access this dot full name, try this out. That's our full name which is from the setup function. As we see, the data section has access to our composition code. This also applies to other sections in the options API, not just the data. We could access our setup code inside methods and computers, and this would also work too. But what about the other way around? Again, let's try this out. We can reinstate our name, and then we can try to access this data property inside of setup; this dot name. We also need to change the name to be full name, and we see a blank screen. Perceives any errors inside of the developer tools. We cannot read property name of undefined. Okay, so let's try without this keyword, refresh. We still have no access to this name. The problem we have here when we do things this way around is the setup function is resolved before the options API. So the setup function cannot access any of our data properties. This keyword is also not available inside a setup. If it was, it would behave differently to how we would expect it to behave inside of the options API, so it has been removed to avoid any confusion. So just to be clear, we cannot use this keyword inside of the setup function. But as we've just seen, it does work the other way around, and we can access our setup properties inside of our data or our options API. We can clean up this code. We don't need access to the full name and move this from the return statement, the data section, and also the output inside the template. This is an introduction to the composition API. In the next section, we're going to push on with our project, and we'll also get lots more practice using the composition as we progress through these videos.
105. Section Intro- Pushing On With Our Project: I like to give my courses very hands-on and practical. In this upcoming section, we're going to put all of the composition knowledge which we've learned into our creative cards projects. We will be pushing on by grabbing the selected card, which the user clicks on. We'll retrieve the pages. We'll switch between the different pages for the cards. We'll add our text inputs for our sections, our text outputs so the user can preview them. We'll allow the sections to be updated, rearranged, edited, and so much more. This will be a good chance to reinforce our composition knowledge alongside building our project. I'll see you in there.
106. Linking To Selected Card & Category Styling: For this upcoming section, we will be pushing on with our project and reinforcing what we've learned so far about Vue and composition. Beginning with a little styling for the categories, currently, we have two files which need a grid style layout, and this is the category.vue. We have this class of grid wrapper, and this is going to list out all of our cards in a grid style layout. The same over in the AppHome, make use of the same class just here. The app.vue has a style sheet down at the bottom which doesn't have the scope attribute. So I'm going to add the styling for this grid inside here. Therefore, this will mean it will apply to all of the components which use this class. So let's add this in, and this is going to go inside of a media query. The grid style layout only applies when we get over 600 pixels. The first media query we're going to apply this to is the minimum width of 600 pixels. Grab the grid wrapper. First up is display to the grid, then the grid template columns. For this particular media query, we're going to place two cards side-by-side. I'm going to set this to be repeat. One needs to be repeated twice. Then if we pass in a one fractional unit, both of these columns will take up the same available width. Putting space in between these cards will also place in a grid gap of 10 pixels. Over to the browser, we now see the initial layout of our grid system. Also, if you click on any of these cards, we see the same applies to this component too. Next, we will have more available space. I'm going to add in a second media query. Let's copy this section, paste this in just below. For this, we'll make this 1,000 pixels. We already have the display type declared and also the grid gap. All we need to do inside here is the changes to be three columns wide. Then when we stretch this to be over 1,000 pixels, it then switches to be three columns. Next, back over to the homepage, we're going to set some more specific CSS for this particular component into the AppHome. Put the style section down at the bottom. This is just regular CSS. Each one of these items inside of our grid has the class of category item. We're just going to set a background gradients around each one and also add some padding to space out each one of these categories. Let's grab this, the category item. The background is going to have in a linear gradient, which will allow us to transition between two colors. The two colors, I'm going to pass in RGB values on the first one of 253, the green, 207, and also 207 for the blue too. Separated by a comma, the second RGB value is going to be red of 140, 140 for green as well, and also 236 for the blue. This gives us the gradient from pink to purple, and also, the color is gone from the top to bottom. I'm just going to offset this by placing in a angle as the first parameter of 30 degrees. This will just shift the color to eight different angle. Next, some padding to give this some spacing from the text of six rems on the top and bottom. We don't need anything on the left and right since we're using this grid layout. This category item is applying styles to each one of our list items. Now, to add some space into the very top of this unordered list, I going to now select the unordered list in place in one rem of margin at the very top. This will give us our space in from our top header. The text for each one of these is surrounded in this router link. Remember that the router link produces a HTML element. We can also use this in the CSS. All we're going to do here is to set the font size to be 20 pixels. It's a little larger. We can still click on these and go through to the individual category too. When we do go through to this page, this is controlled by the category.vue component. Currently, when we click on any of these individual cards, we just have the router link pointing to the forward slash. But what we want when we click on any of these cards rather than this to be taken it to the actual cards so we can edit. The edit screen for any of our cards is this create component, which we added early on. Now, let's go to our category and we can change this router link to be dynamic, close on the sidebar, and then place in it the backticks. We need to go to the /create, has now a variable which is going to be the card.id. Remember, we got this card variable from this loop just above. The first thing we want to see is the front page of this card. Then later on, we'll also add some links to this create page, which will allow us to switch between the front, the inside left, the inside right, and also the back too. Now, with this link in place, we need to go over to the routers index page, and also changes create component to. Rather than going to /create, we also want to grab our variables, which is the card id and also the current page. The first parameter is for our id and the second one is for the page. That's my result. Anniversary, this is the card with the id of one, but then taken to the front page. Let's try Halloween. This is card number 5. This is all working fine too. We will focus on this create vue in the next video. But for now, let's go back to this category section where we're going to finish this grid off by adding a preview image rather than the card name. Over in the assets folder, into the images, we have this card preview section. This has a image with a preview of the front page of each one of our cards. Also, each one has the title with the corresponding id for that particular card. All we need to do is to access the card id and then link it to this file path. Into the category section, rather than outputting this card name, we're going to pass in the image element, pass in v-bind to the source, and then inside of the backticks, we can add our dynamic URL. The ampersand is going to link to the source, into the assets folder, the images, the card previews. Each one of these card previews has a name which links to this card.id. Also passing in the file type too. Back over to the browser, we don't see any images display in. We have a spelling mistake here. Try again, and we still don't see any images for this display. Now, this happens when we use in a dynamic image just like we are here. We could place in a static image string with no variables, and this would work completely fine. By static, we mean if we don't have this colon and the binding, and we don't have any variables inside, we may just link to the 1.jpg with the backticks. This one works completely fine. This dynamic file path fails because behind the scenes, we use in what is called webpack. Webpack is used to bundle together all of our files and all of our assets from the source directory into one big JavaScript bundle. This is any bundle which is served to the browser when we visit our website. But the problem is when this file path is dynamic, webpack doesn't know the results of this variable are compiled time. At this compile stage, it knows there is a variable inside here, which may change, but it doesn't know the exact value just yet. There is a couple of ways we can fix this. We could move the images to the public folder and this will work completely fine. Or instead, we could allow webpack and tell it we need to pass this dynamic string by roughness in require. What we need to do for this is to put out this full string in the backticks as require. Then inside the brackets, we paste in our string. We see straight away over in the browser, it would now have our images on the screen. Of course, they are very big. So what we're going to do is go over to the app.vue and also target any images which are on our application and set the maximum width be 100 percent. Save this. It now leaves a nice grid layout for our cards.
107. The Create View: Clicking on any of these selected cards will now link us to this Create component. This page is what we now want to work on. We're going to split it into two sections. The left section will have a card preview, which will show the initial cards and also any changes which we'll make too. Then the right section is going to be used to edit this card, including the images and also the text sections too. For these sections, for the preview and the edit section, we're going to create two new components into the components folder to keep these organized, a folder called card. This will group all of our card-related components. A new file. One is the CardEdit.vue. Also, the second one, also in the same folder is for the card preview. Now, both these are just going to have a simple template with a level 1 heading. This one is the card preview. Grab this and save. Then we can place this inside of the CardEdit. As we just mentioned, both these components are going to be displayed inside of this Create.vue. To begin to create a script section, which is going to be used to import both of these components, the first one, the CardPreview into the source, the components, the new card folder, and the CardPreview. Duplicate this. This one is for the CardEdit. Inside of the export, we'll then register our components. This is all we need for our script for now. I would go up to our template. I'm going to create a div with the class of card_wrapper. This is the main wrapper div for both of the sections. We'll have the preview on the left and the edit section on the right. This just means we could target the section with our styling. First, the left section, and then the right section. The left is for the CardPreview. Now, place the CardEdit section on the right. Let's see if everything is okay. Refresh. We have the CardPreview and the CardEdit components. Of course, these are not laid out on the left and right just yet because we need to go down to the style section and add some CSS to this card_wrapper. It's going to also be scoped. To begin the display type of flex, and the default flex direction is row, which will place these two components across the page. Also, this main wrapper is going to have a background color. We'll get the simple and go for a light gray color of EEE, a box shadow of one pixel, one pixel, two pixels. The color of the DDD, and the shadow to be inset. This means the shadow will be placed inside of our section rather than on the outside. Next, we'll take a look at the finished version and go into one of these cards. We see that both this section on the left and the section on the right is both equal. To achieve this, we're going to grab our card_wrapper, and each one of these was wrapped in a section. Make them both equal. We can pass in a flex value of one and also a little padding on the inside of 10 pixels. Refresh. Now, this flex value of one will apply to both of these sections, giving them equal spacing. Now, we have this layout all setup. In the next video, we're going to actually display the selected card onto this page.
108. Retrieving The Selected Card: Upon selecting any of these cards from our grid, we then take into this great view, and we also have access to the ID of the selected card. This means we can use this to get the card which we need to display, and this can also go inside of a composable file 2, into the sidebar, and then into the composables, create a new file inside here called, use Current card. Remember these have the js extension. Setup an export, with default function called use Current card. The job of this function is going to be to get the selected card, to retrieve this, we also need to access all of our cards, so we can filter down the selected one, we can possibly use cards composable, into this section. Only file path, which is going to be dot slash, and the filename of use Cards. Remember the use Cards has returned to this all cards variables, now we need to go back into our composable, and then extract this from our function. We now have all of our cards and what we need to do now is to create a second constant, which is going to store the selected card. This selected card will also be an object, we can pass in an empty object as the initial value. Since we are using a ref, we also need to import this from the view library, and then we're going to filter this all Card section that with the selected card. First grab the selected cards, and we can set the value, to be equal to all card. This all card is also erupt in a raft 2, we also need to use the dot value to access the result, and make use of the JavaScript filter method which is going to take in a function, which is going to filter these if the card dot id is equal to something. Now we just set this to a static value of one, back over to have a great dot view components. We can now check this is working by importing this composable, and then display this inside of the template. First import this. It says use Current card, from the file path into the source composables, and then use Current card, into our exports, for the setup function. Infact, we forgot to do one thing, and that one thing is over and use Current cards, we also need to return back a value to use in our components, and the return value is going to be selected card, I hope you spotted that one, back to the create section into the setup. Will now extract this into a constant. To display this in the templates, this also needs to be returned back from this function, and then place it anywhere in the template above, just to check this is working. Let's check this. We see an error in our composable, all is defined, but never used. We can also autocomplete that. Refresh and there's our selected card. Good, this is all of the details from our card with the ID 1, which we set just here. But notice how our card object is also surrounded in these array brackets. This is because our all cards data is an array of objects. To work with this one single object, we need to pull it out of this array with districting. We did say before that this selected card needs be a object, and we distribute this outs of our array, by surrounding this in the square brackets. Refresh and these brackets are now gone. Next, rather than using the hard-coded value of one, we now go to import the router, and get the ID from the params. First import this. We use equal route. This is from the view relative package, and then store is in a constant so we can access this inside of our function. This routes can now be used to access the params rather than these hard coded value, route params, and the parameter name which you gave this over in the routers index page, was the ID of the browser, and it just wants to be use route. The card has now disappeared. Let's try one more. Now, we did have a card on the screen just before, but now since we replace this with our router, we don't see anything on the screen. Now, this is one of them, web development gotcha moments. What we need here, is a card ID, which we get in with the router.params.id. We need this to be a data type of number. In act, when we read anything from our URL parameters, this is returned back as a string. We can see this if we do a console log. Just anywhere inside this file, access the route params.id, and we can use JavaScript to track the datatype with type of into the console, refresh the browser and we see the data type is string, and this is why all filter is not working because this is comparing, with the strict equality we check in if the data type of string, is equal to this, which is a number. We could just use the double equals like this. This would ignore the datatype difference, and just use the value. Or alternatively, we could grab this, wrap this inside of the number function, and this would convert the string to be a number, and this would also work to. Let us check a couple more cards. This will be the third one. We see the ID 3. The fifth one, ID 5. This also matches up to.
109. Selecting The Current Page: In the previous video, we said this create page displays the full information from the selected card. But in fact, for this view, we only need to show a certain piece of data. Remember that our card is split up into four pages. We have the front, the inside left, the inside right, and also the back view. When we are editing this page, we only want to see one of these sections at a time. Only when we click on these cards, we're then taken into the view, which is the front of the card. The data for these four pages is stored inside of this page's array just here, and retrieving the exact page we need from this array is what we're going to focus on in this video. We'll then also add in some links so you can switch between these pages. To start things off over in the useCurrent card file, read the constant to store the selected page in. Now we don't need this console log. This constant is going to store in the selected page. This will also be dynamic and a date type of objects that's obey function called get selected page. This function is going to set our selected page. This is a ref, so we grab the value, and this is going to be equal to our selected card, the value. As we've seen in the browser, we also need to access the pages array inside here. We then need to get the index number for the selected page. We have four different values for the front, the inside left, right, and back, so this could be either zero, one, two or three. To get the index number for the particular page we need, I'm going to hand it over to a separate function, to keep things clean. The function will be called get page index. To get the page index we need, we need to grab the selected card, the value, the pages, and then use a JavaScript array method called find index. Find index is going to run a function for each one of our pages in these cards. Each one of our pages will be stored inside of this page constant. We want to check if the page.name is equal to route. params.id. Just to recap, we are accessing all of the pages in the selected card. We then use any JavaScript, find index method, and this is going to check if the page.name. Which you can see if we go into our data, each one of our pages has its name property such as front, and we're comparing this name to the value inside of all parameters. In fact, rather than ID, this one to be the page, such as the front which we have just here. This will then return back the index number, so I need to return this back from our function. We can access this return value, my call now function inside of here. Good. Now we have this function called get selected page, and now we need a way to actually call this. Well we can do this by using unmounted assuming our function called get selected page. It's going up and bind has imported automatically when saving, but if yours hasn't, if you were to add in unmounted to this import. Now we also need to return back our selected page, so we can use this in the template. And also add a semicolon if this is causing some problems, and over to the Create. view. Now instead of rendering out the full selected card, we can replace this with the selected page, returning us back, and then I'll put this inside of a template. Refresh. Good, there's the front. Let's try the back. Good, the inside right. Excellent. Now this is working and we can now set up some links in the next video to switch between these four pages.
110. Switching Card Pages: We now know from the last video that we can retrieve the correct page we need from the URL. Now we're going to setup some buttons to switch between these four pages. This, a new component, a new components folder, and then into the card folder. This one is going to be called switch page. Setup the templates that you're just going to [inaudible] link so we can pass in the nav element, the unordered list, and then our list items are going to be wrappers for the router link. Each one of these router links is going to link to the four pages of our card. Pass in the to prop. This is going to link to front, and we'll take a look at this in just a second. But now a class of page select is relatively equal half to the text to the front, alongside a icon to. This icon has been provided with this course and you can access this from the assets folder. First, the text inside the span of runs, and then display the icon with the image. This one doesn't need to be dynamic, we just need to go into the assets, into the icons. The same icon is going to be used on the front and the back of the card, so this one is called front-back.svg. Place in some alt text and we can now duplicate this for all the three remaining links. Copy and paste the full list item. This next list item is for the inside left. The icon name is inside left. The alt text, copy this one, then change all of these occurrences to inside right. We have the router link, we have the text, the image source, the alt text. We can also fix this typo too. Finally, paste in the last one and this is for the back of the card. We know what to do now, the router link, the text. Remember, this one shares the same icon as the front, so this name was front-back. Then wrap up this last link with the alt text. Save this file and this component is going to be placed inside of the create.view. First import this. Once you duplicate the CardEdit, this one is for SwitchPage. There's the path and also import this into our components object. Place this at the very top. Over to the browser at the front, inside left. This is also changing the URL, the inside right, the back too. Notice how each one of these links is just replacing the end section of our URL. This is because when we added the router link, we have not used a forward slash before any of these routes. If we did use a forward slash, just like this, click on the back. This will then replace our full URL. But leaving this out will only replace the section which we're currently on. Another thing to note here is when we click on any of these links, the selected page is not being updated. The reason why this doesn't update is the same reason as earlier when we tried to upgrade these three category links. This is because we are still on the same create components, so when we just switched the parameters like we are here, no update will occur. We can also fix this with the same solution, which is to add a watcher, or to useCurrentCard at the very top. Import watch and we can watch for a change of parameters. First pass in a getter function which is going to watch for any changes to all router parameters, and in particular, our page. Next, a callback function, which is going to run each time there is a change, and its function is already setup just above. This, and add a semicolon just after our watcher. Keep this separate from the below expression and then over to the browser. Refresh. We see the front because we're only front URL. Inside, left, inside right, and also, the back is working too.
111. The Card Preview Component: But over to this create.vue components and inside here, we're displaying the selectedPage. Instead of showing this just like this as a object, we're now going to move this and pass the page down as props to our two components, which was the card preview and also the card edit. First, remove this, pass this down first to the card preview, the proper name of selectedPage, which is equal to selectedPage. The same just below for the current edit. This prop will give the card preview all the information it needs to display the card and over on the right, the card edit section will use this prop as the initial value for all of our input fields. If we wanted to, instead of setting this up here and passing this down as props, We could also import these directly into our two components too. But for this, I'm going to stick to props. Into the card preview. This needs a script section to accept these props, pass these into our object. The type, this objects, and also, this is required too. Up to the template, we don't need this level 1 heading. Instead, replace this with a div and this div is going to be the wrapper for our card. We need to add the background image, just like we see if we go to the final version, into these cards. This will be the wrapper for our preview, and this will have the style attribute to set the background image. First, a class of card preview wrapper and then bind a dynamic style, which will take in an object, and it's inside of the style object where we're going to pass in the background URL inside of the bactics. Just like we could with regular CSS, we can set the background URL. But because this will be dynamic, we also need to set this inside of the bactics, place in our variable, which is the selectedPage of our props. Select the background property. Next the background size, which will be a regular string of 100 percent. Next, the background repeat. Also, remember, since we're doing this inside of JavaScript, we need to make each one of these camelCase. If this was regular CSS, we would do it just like this with the hyphen. But since we're using JavaScript, we need to make each one of these camelCase. The value for this will be no repeat. Then finally, the background position. This will be also dynamic since we have this set inside of our data. Go down to the data file. We grabbed the background as before, but now we're going to grab this background position. This is also extracted from all props. Now, inside of this div, we need to loop through each one of our sections. In this selectedPage, we're going to go and see this inside of our data. This front page has this array of sections. Most of the information in each one of these sections is for styling and CSS. But the one which we're going to use to display the actual text from a section is this user input. I'll place inside of here a div with a v-for, pass in in the section in selectedPage.sections. Each one of these sections has a unique ID. So the key will be section.id. To check this is working, I'll put the full section, refresh. We can just about see a background image and our text overlays. But we do also need to give this background image a width and a height. Back to the components, we can access the card preview wrapper. Then set a style section at the bottom. This can also be scoped. First, the width for this image is going to be fixed at 450 pixels, and then a height of 600. Good. We can now see the full size of the image. The final thing to do is to set the overflow property for this section and this will help us out later on if the user increases the height of any of these sections to be higher than the card. Great. Now, this is all working. We see also that the front of the card is the only one with an image and that some of these sections don't have any text at all. All of these text sections, we don't want to show the full object like this. Instead we just want to output the user input, which you can do it like this, section.userInput. Okay, great. This text which we overlay will be soon placed into a separate component and we'll also apply to it all of this extra data such as the styling, and also the layout.
112. Text Output Component: What we have at the moment is this card preview which displays a background image. Then just below for each one of our sections on this page, it will then output the user input. We've all seen something like this. If we go to the front page, we see the background image. Then the text overlaid for pages with multiple sections of texts like the inside right. We see these displaying just here. This text though is not intended to be displayed like plain text, just like this. If we go to the data.js, we have an array of sections with an object, and this particular one has this user input just here. Then we have various things for styling and layout. To show these, we're going to create a new component to output each one of these pieces of text over to the component section and into the card. This is the text output.vue, the template, and just some simple text for now. This is going to be displayed inside of our card preview in place of this text just here, into the scripts action, import this file, which is the text output. This is in the same folder so all we need to do is./, and then into our text output, register our components, and back up to the template, we don't need this section, we don't use inputs anymore. Replace this with our new component. Of course, this text output needs some data to display, so we'll send down the full contents of the section as a prop. This is all we need to do for this component. Over to the text output. The scripts, place in our props, and the section prop will have the type of object. Remember, this is the shorthand for the type. Rather than having the object, I'm placing the type just like this. Just like in the card preview, if we now output the contents of the section, we should also have access to the user inputs. Let's see. Good. We have all three separate user inputs and also, the same for the fronts of the card too. This leaves us in the same position as the beginning of the video. We have just the plain text home, but this time from a separate component. We can now use these CSS values we see in the data file to create a CSS object and apply it to this text. First, place in our setup function and a constant to store our CSS object. Keep an eye on any changes. We're going to make use of a computed value inside of this setup function. We'll pass in a function and this function is going to run each time the data inside changes. Just like when using the Options API, this also needs to return some data, but I'll just place in an empty object and we'll come back to this in just a second. Before we forget, place number returns action for the setup, where we're going to return our CSS object, because now we use computed, we need to import this from the view library into our returned CSS object. This is going to be an object with all of these CSS properties which we can pull from this section. But first, we have a couple of static CSS values which are not going to change, but just a border which will surround each one of these pieces of text so the user knows the height of the section as a string, the one pixel width, a dotted line, a gray color, and then separate each one of these with a comma. The display type is going to be equal to flex. This flex type will allow us to make use of the flexbox properties, which are over in this data, and in particular, the values for alignments such as justifyContent and align items. Since we are now returning the CSS object, we can test this is working up in our text file. A CSS style property and set this equal to our object. If we now refresh the browser, we see the dotted line, so our CSS object is now taking effect. The rest of the styling to apply to this text is going to be dynamic and based on the values which had been passed down from our props. For example, the CSS color property, we could normally access this using the options object with something like this.section. Then we could access the individual properties, but remember though, using the settle upon composition, we'll no longer have access to read this keyword. The way to do this in composition is to pass in the props to setup. This will then give us direct access to our props, where we can access the section, and then one of our values on this section object, which is the color. Next, the height of the section, props.section.height. Since we're working with CSS, we also need to add the pixel value. After the height, we have justifyContent, which is equal to prompts.section.justifyContent. Align items. Then save this section and test this out over in the browser. Good. We see we have various heights for these pieces of text. We have a different on color. Over to the front. Good, we have a height just on here too, and this is also aligned into the center, so our flexbox properties are taking effect. Let's also place in the font family and the font size. The font size. Place in the comma, and we now see a larger text, and also the font family has been applied. Don't worry about the spacing between these sections or the bottom one being off the page for now. We will fix this with CSS in a bit. Though we're now done with our style object and this computed section will keep the card preview updated. When we update the card from the edit section later on, all of these values will be updated in our composable function. Then the reactive values will be passed back down the chain to this preview component.
113. Card Edit Component: We'll take a look at this create.vue component. Early on we passed in the CardPreview and also the CardEdit. We also passed in the selectedPage to both of these components. In a previous video, we used this selectedPage, looped through each one of the sections and then I'll put the text in each section. We're going to do a similar thing for the CardEdit, which also has the same props. For this time, it's going to be on the right hand side. The text is going to be placed into a text area which the user can edit. We can also hover over this section. We'll look at a drop-down menu to edit any of the styles too. But this section, we setup the CardEdit.vue components early on. Let's get to work on this component by first adding our scripts action. I need to take in the props as an object, pass in the selectedPage, which is the type of object. But now this template will be pretty straightforward. We're just contain place in a div, which is going to contain a v for attributes. We can live through each one of the sections in our selectedPage. The key, we all have a unique key for each one of these sections which we can see in the data. We can access the section.ID. But I'll pass in the section.userInput just to check this is all working. As we just mentioned a bit later on, this was placed inside of a text area so the user can edit this text, save this and the single piece of text for this card is now displayed all on the edit section. Let's try the inside right. This is all working too. A pretty straightforward video, but we now have the text available on the screen. In the next video, we're going to replace this user inputs with a separate component.
114. Text Input Component & Hover Menu: Currently, in the Edit section, we are simply outputting the tags from each one of these sections, but just like we did with the preview side, we're going to replace each one of these pieces of text or each one of these sections with a stand-alone components. This component is going to contain a text area which will contain this user input so they can edit the text, which will then display on the preview. We'll also place in a drop-down menu too, so the user can also edit this text. They can add different styles and also change the layout and positioning too. Back over to the sidebar, we have the text output for the left side. So we're going to create a text input for the edit screen, a template, then a div with the class of text input wrapper. Place in any text inside here and then we can import this into our Code Edit component. Down to the script, the text input. This in the same folder, so this will be a dot slash text input. Last in the components objects, replace our user input with this new component. It also needs the full section data, so this can be a prop. This now gives us some information to work with, placing these scripts section, the props of section, which is the type of object, and then replace our text with the text area, move these. Then set the default columns to be 50 and then four rows. Now, see this user input inside of his text area. We now need to place inside v-model, but remember we can't directly mutate props. Instead, we need to take a copy of this section. Then we can use this for our v-model. This we can pass in the setup, taking the props, and then a constant which is going to be a copy called the current section. Wrap this in a ref. We're are going to pass in the props dot.section. Import this ref from the View library, then return our current section back from set-up. This current section is now a copy of all props, which we can now use inside of v-model. The property on this section is the user input. We'll load the browser and we now have our free text areas, each one with the text inputs. Also, if you're wondering what this green circle is on the right, this is a Grammarly extension, which is available to help with any typing errors. Something to note here when we type into one of these text areas is, the current preview will also be updated automatically too. Now, I haven't admitted any custom events. We haven't created any functions to update this, but this comes back to the object reference we discussed earlier. We could stick with this and allow this to update automatically, but personally, I prefer to break the reference and do a manual update just so we'll have a little bit more control over the updates, and it makes it a little bit more predictable. To break this reference, what we're going to do is go down to the setup and rather than directly taken a copy just like this. We're going to cut this out, create a new object, and then spread these back in. We can test this out, refresh. Now, we're going to update these text areas. The previous section is not affected. Along with obtaining in this text from the text area, we also want to provide a drop-down menu when the user hovers over each one of these sections. This drop-down menu is going to contain all of the options to change things like the fonts, the colors, and also the positioning. This, create a div inside of the template, just below the text area. If you say class of menu, we need a way to control this menu popping up. We're going to add the V-show attributes and set this equal to Show Options. For now, just some texts like menu is completely fine. Then down to the setup function, we can now set up Show Options to be an initial value of false. This will also be reactive, so pass in the ref and the initial value of false. Then we need to return this from our function as this. We don't see our menu texts yet. We need a way to set this to be true when the mouse hovers over this section. For this, we have access to the mouse over event. When the mouse passes over this div section, we're going to set, show options to be equal to true. Then we also make use of the mouse out event, which is going to set the show options to be equal to false once the user moves the mouse away from this section. Therefore, this will then a close the menu like this and we can try this out. Over any of these sections is our menu, which will then disappear when we move away from the menu. Let's try this one. This now works for each one of our sections, and it is menu components, which we are going to be building next.
115. Adding Menu Options: Let's now move on to the menu for each one of these text input. We've already setup a hover, which then displays our menu, and now we're going to add some options to this menu. This was a div which we created in the last video. Surrounded inside of this div, we're going to place in three new sections. The first one will be a div with the class of menu top, the second one, menu middle, and the third one, menu bottom. There will be quite a few different options we'll add to this menu. In this video, we're going to focus on this menu top. Menu top will be the top row of our options and will contain the font family. The user can change the font size, the font color, and also the bold and italic options too. First we pass in our select option, which is going to group together all of our font families. All we need to do as an attribute is to pass in v-model, which is going to link to our current section. Then the font family. The first option. Then inside the values we need to write in the font family in the exact same way you would be in CSS because this is going to be pushed to our card object. Just like in our data, if we look for the font family, this will be the final values we see here. The first one, I'm going to go for Times New Roman. You can change these to be any font family you'd like. Also the display text, duplicate this. The second one of Georgia. Number three will be Arial. Let's go a bit crazy and add in Comic Sans. Again place this in exactly the same as you would do with CSS. The Verdana font family for number five. Then the last one I'm going to place in Courier New. Okay. Let's try this out, refresh the browser. There's our select with all of our font families. Next, just after this select, we place in a new select group. This one is going to be for our font size. Again, this needs to v-model so we have all the data to push to our card object. We'll select the current section, then this time the font size. The first option of 1.6 rems. Then the text of normal. Next, the text of big with a value of two rems. The next one will go for 2.5 rems. The text all bigger. Then biggest, which is going to be three rems. Okay, let's try this. Good. The third input is going to be an input with the type of color. V-model this to the current section and select the color. Okay. So this and then we go. This just now leaves us with the italics and the bold elections. First we'll add in a label. This one is going to be a checkbox nested inside for our bold selection. Parsing the input, the type of checkbox, V-model. For these next two, we have the is bold and italic options, which are just build on values. What we're going to do for these is add an input alongside an image. Now, the reason we're doing this is because the image is going to be one of the icons which you have in the assets folder. But we still also want to keep this inputs alongside for accessibility reasons. This means it can be read by screen readers and also keep the keyboard interactions too. But in the CSS in just a bit, we'll hide this input. We only see this icon just below. For the source added into the assets, and so icons. We have a provided icon which is called bold.svg and the alt text bold icon. Next, we also have a checkbox for the is italic. To speed things up, we can copy this section, then change is bold to be is italic. The italic.svg, and also the alt text too. Save this. Let's see where this leaves us. Okay. So it looks pretty horrible at the minute, but we'll fix this with CSS. There's our check boxes alongside our icons. But like I mentioned before, we will hide this checkbox later on. Something to note here is, especially when using Chrome, it may not be the same in different browsers. I believe it's not with Firefox, but when using the Chrome browser if we click on this color picker and then try to select on the colors, the whole menu will disappear. Now Chrome holds a huge market share for the browser market, so this is obviously something we want to fix. What is happening here when we move from this color input to the actual color selector? The selector is a separate section. Moving to this new section means we're leaving this main wrapper, this then triggers the mouse out event, and then hides have options. Workaround for this is to watch for the mouse leaving this menu top section. Then once again, set any Mouse options to be true. Add a mouse, leave events inside here. We will set the Show options V equal to true. Save and test this out. Click on the input. Now we will move away from this top section. The Show Options is still set to true, meaning we can still see our color inputs. We have this now fixed. The final thing we need to do is to check if our current section is being updated with any of these inputs. I'll place this inside the double curly braces in the template. All we need to do is to amend our text. We see the user inputs is changed. Click on this bold, and this will toggle this just here italic. The font family, this changes to Arial. Finally, the font-size. This all seems to be working, and in the upcoming video we'll add the rest of our menu options.
116. Menu Options Continued: Moving on with this drop-down menu section, we still have the middle section and also the bottom section too into our text input. Then if we go to this menu in the middle section, this is going to contain a up and a down arrow. But now they're just going to be a set of icons, but later on, these would trigger a function. To change the height of the section, wrap these in a button, and the icon is also in the assets folder, into the icons. This one is the arrow up.svg, the alt text too. Then the second button is for the arrow down. Save. There we go. Then finally, we have this Menu button. This Menu button is going to be mainly alignment, so we can move the text up and down, and from left to right too. These are going to be two sets of radio buttons, one for the vertical alignment and one of the horizontal alignment. So I'll pass in a label, which will wrap our input with the type of radio, the model, which is going to model to current section. Then justify content. Justify content will control the positioning across the page from left to right. Then we pass in a value. The value will be the text we passed to our card object and this needs to be one of justify content's CSS values. So this one will be flex-start. Then the name of horizontal. This name of horizontal will be used to link together all three of our horizontal buttons. Then we'll have some vertical bonds for the vertical alignment. Just as we did above with the checkbox, we're also going to hide this radio input too and replace this with an icon. The icon goes in the image elements and the path is in the assets. Into the icons. The left icon.svg and the alt text too. So this one is controlling the left alignment and we're going to copy this and paste this in two more times, and the middle one is for the center. So we're still justify any content, but the value this time is center. These three buttons are related, so we keep the same name of horizontal. This means the user can only press one of these buttons at a time. The image is the center, the alt text. Then the third one will set the right alignment or in flex-box language, this will be the flex end. The image has the name of right, and also the alt text too. Now, let's see if this works okay in the browser. We can only select one of these at a time since they all have the same name and these are also updating our object at the top. So the next group is going to be for the vertical alignment. If we copy one of these, paste this just below, we have a couple more changes to make. Rather than setting it justifyContent, we need to change this to be align items. The first value is for the flex-start, the name of vertical. This icon is for the top. Then it duplicated this one two more times. The next one is for the center. The icon has a name of middle. The final one is for the bottom, which is the flex-box value of flex-end. The image is bottom.svg. Finally, the alt text. Over to the menu. It's looking pretty bad now, but let's try this out. For each one of these, we see the flex-start, the sensor, and also the flex-end too. Now we have this menu in place and we know it's now linked to our current section. Next we're going to move on to improving the styling.
117. Menu Styling: This menu now needs some styling and also some layout too, which we want to do over in the textinput.view, starting with this top row which has the class of menu top. So I'll jump down to the very bottom, create the style section, and also add in the scoped attribute. Remember that each one of our icons are surrounded in the image element, so what we're going to do for this is to set the maximum width to be 30 pixels. The full menu was surrounded in this menu class and this was a wrapper for all of our inputs. First of all, let's set the position to be absolute and by setting this to be absolute, this will float above all of the rest of the content rather than push this down. What we mean by this, if you go to a project, if you have multiple inputs, like we do just here, the icons will appear over any of the content just below, rather than push this down of the page. The background. You can choose any color which suits you, but I'm going to place in a 30 degree linear-gradient and the values of RGB 253, 207, 207, add a comma, and the second color of red, 140, green 140 and blue, 236. This also needs some padding inside and we'll round the bottom left and the bottom right corners too. Padding of 10 pixels and the body radius, we only want to apply this to two outsides, so the top will be zero, and then three pixels for our bottom. Okay. Next, we wrapped each one of our roles in the class of menu top, menu, middle, and many bottom, and we can make use of the CSS flexbox to set the alignment across the page. All of these inputs are different heights, so we won't need to be across the same center-line inside of our style. Grab our menu-top and also, the menu-bottom, the display type all flex, and then align items into our center. Next this middle row, where we have the up and down arrows, this needs to be over on the left of the menu and we can do this by setting the display type to be flex. Grab the middle section, set this to be flex, and by setting this to flex, we'll have the default values of flux row and also appear at the start of the row too. Now, we also want to remove the border and the background for these two buttons too, and also change the cursor to be a pointer. We could also do this inside of this same components, but I'm going to place this over inside of the app.vue, and therefore, this will also apply to other buttons too. So let's grab our button. Remove the background, remove the border with none, the cursor of pointer. The font size of 14 pixels, and then remove any default paddings with a value of zero. Refresh. Okay. So this also maybe wants some spacing too, so back to our text input. Grab the menu middle, and we'll select the image. The height of 10 pixels and then some padding. We don't want this padding to push this away from the left edge, so we're going to apply the padding to the top, the right, and also the bottom. There we go. This now gives us a little bit more spacing. Just to clarify, when we added this button style over in the app, it's not essential that we place any global styles inside here. It can just be a convenient place to put non-scoped styles if we don't have a huge amount. If we do have lots of different styles, this maybe a better idea to maybe outsource this into a different file. The next thing we're going to do is back over in our text input and we're going to hide the check boxes and also the radio buttons too, even adjust these icons. Remember from earlier, we said we leave these in for accessibility reasons and so the user can also make use of the keyboard controls too. The reason these are still accessible and we can still control these with the keyboard is because we're not completely removing them. Instead, we're simply hiding them with CSS. So the first step is to select our input with the type of radio, and then also our checkbox too. So this is no longer visible. We'll set the opacity to be equal to zero, the width of zero too, and also the height. Let's see if this works. Good. These are now removed and the next thing we're going to do is to select the image and make the cursor a pointer, and to do this, we'll grab both of these, add these in below. Make use of the adjacent selector, which is the plus icon, and this will select all of the image elements which follow this radio. The same for the checkbox. The cursor to be a pointer. This will not change when we hover over our check boxes and our radio buttons. [inaudible] any original form inputs like this, we also need to handle the checked state and if we click on any of these, we don't see the buttons are highlighted. So if the user was navigating with a keyboard, they would have no idea which one is currently active. So to fix this, we also need to account for the checked state. So grab our two inputs and the image. Then we'll create a new section just below, but this time, we'll target the check state. Now, what we're going to do inside of here is to set an outline when any of these images has been clicked on so the user knows that this is being selected, of two pixels, a solid color, and then a color for this outline. I'm going to go for the red value of 119, 123, and also 125. This is getting very close, but it's not quite there just yet. This is going to place an outline on the button of the image when a user clicks on any of them, but we also want this outline to appear when the user is using the Tab button on the keyboard to cycle through these buttons. So again, we'll copy both of these, add a comma, paste these in again, but this time, the focus state. Save this file and over to the browser, let's check this out. Hover over the menu and instantly, we see our outlines for the first group and also the second group too. We can select different values and these will also be highlighted too. Also if we use the Keyboard tab button to cycle through our website, once we get to the menu, we have the options, the bold, italic, our open-down buttons, and then if we hit "Tab" once more, we can use the left and right keyboard buttons for our first group. Hit "Tab" once again, and the second group becomes active. Good. Just to finish this off, we'll also add some styles to our text area, and also these select buttons too. First, the text area. Move the default border with none. A little bored radius of three pixels. The width of 100 percent. The font weight of lighter. Five pixels of padding. Also for the sizing, we'll set the box sizing to be equal to border-box. Then lastly, these select inputs are a little bit bunched up and all I'm going to do is to do is to add some margin to the right. Five pixels should be fine, and there we go. This is the styling now finished for our menu, and next, we're going to set up the card updates.
118. Updating The Card: We can see from all of these objects over on rights here, that all of these updated sections are stored inside of the text input components. We can now use this data to update the card with all of this new updated information. A good place to do this would be over, inside of the use current card. We will have our selected card, which is our main card object. What we're going to do is create a function to select this card, and then we'll get the particular page which we're working on, and then update whichever section this data is sent from. This function is going to be called updateCard. Since this card is a raft, we grabbed the selected card, access the value, go into the pages array, and the item of which you want to find or the page which you want to find is available from this function just above. Remember, this is going to get the selected index number from the routers parameters. We can call this function inside here. After this, we want to update a particular section. For this page, it will be any one of these three sections. But how do we know which one we want to grab? Well, currently we don't. We don't have any way of accessing this information, so we're going to have to parse this when we call the function. This means this function will receive some data. I'm to call this the sectionIndex. [inaudible] Along with this, we also need to parse in the new data to this function too, which will be used to update the card. We'll set this particular section to be equal to this data. This is all we need to do for this function. Now we can return this, and then we can call it with our sectionIndex, and also the updated data. Place the updatedCard into our return object, and the components we need to call this in, is inside of the text input. As ever, we need to import our composable, which is called useCurrentCard and the file path, use the at symbol to go into the source, the composable folder, and then into useCurrentCard. Down to our setup, extract this into a constant, and we need to extract the updateCard function from the function which is called useCurrentCard. We've got our function now in place in the current components. But if we think about this, when do we now want to call this update function? How do we actually want to run this? We could go over to the card and when this component loads, we could run this function after a certain time delay. We could run the function when the menu is closed and so many other options. But a sensible one may be to watch out for this current section. Remember, all of our inputs from above are bound to this current section by using V-model. Anytime one of our inputs change, the current section data will change too. Therefore, a sensible option may be to watch out for changes to this current section, and then it called this update function. This will need to import either watch or watch effect. I want to use watch, since watch effect will run immediately, and we don't really need that. Into our import, after the ref, import our watcher, and then we call this inside of the setup. Then we parse in two arguments into this watcher. The first one is the actual piece of data we want to watch, and in our case, it's the current section, separated by a comma. We then want to call our updateCard function when any of this data changes. Parsing this function, and we'll also call this too. Remember when we set up this updateCard function over in our composable, it also needs to receive two pieces of data, the section index and also the new section to update. Currently, we don't have this section index just yet, so we'll come back to this. But we do have the updated data stored inside of our current section. We also need to access the value since this is stored inside of a ref. This just leaves us with the task of finding out the current section index. This current file of the text input doesn't have the index number, since all we're doing is parsing at this section as props. To get the actual index number for this particular section, we need to go up one level. This text input is called from our card, edit component. So into this file as our text input component. Since we're looping through all of these sections, we can also access the index number 2. We looked at how to do this early on. We write this inside the brackets, and we can also access the index number 2. This means now we have this index, we can also pass this down as props to our text input component. Section index is equal to our index. Good, now back to the text input and we now have a second prop to add to this, which is the section index, which is a type of number. Now, we can parse this when we call our function. In our setup, we have access to all props, and then our section index. Also, just make sure we're watching the current section of value 2 and over to the browser, let's see where we are now with our project. We're missing the text on the right. Now, let's go into the console and see what's going on here. We cannot set property zero of undefined and no use current card. This is our update card function, we seem to have an error just here. Let's see what's going on. We access in the selected card the value into the pages, the page index. We know this is working because we've used this just above when we get the selected page. This all works fine. Then into the sections. I think this should just be section with an S on into the data. Yeah, we just need an S on the end of our section. Hopefully, we see our text once again. Good. Now, let's try to edit this text, and we see an update. Then you see a font style, the alignments. Good, this works because when we update the card, the card value is also reactive. Any changes are then passed back down to the components which need it. This is why we see the updates in the card preview. But there is two options which won't work just yet. This is the bold and italic. This is because both of these are checkboxes which are either going to be a true or a false value. To deal with this, we're going to go over to our text output and create two new styles. These classes can be toggled on or off. The first one is going to be for bold, which is going to set the CSS from wave property for bold and then the second one is for italic. We'll set the font-style to be equal to italic up to the top. All have inside of our template is our P element for our text. It's inside here where we'll add the dynamic classes as an object. Inside this object, we now have two classes which you want to add. These are our two classes which was set up down at the bottom. We have bold and we have Italic. Now we need a way of toggling these classes on or off, depending if these checkboxes are selected. The way to do this, we will take a look at our object is we have this is italic property and also is bold. Both of these are available on our section object. The first one is section dot is bold, and then section dot is italic. This means if this is true, our bold class will apply and also the same for Italic too. Before we wrap this up, let's say this and try this out in the browser. Let's change the text, become bold, and this works too. Become italic and this will toggle the class on and off. We can also see if we go to different text inputs that all of these sections are completely independent too.
119. Adding New Sections: Even though we have supplied the user some sample cards edit, which is all these ones used here. We want to also make them more flexible by also allowing them to add and remove sections too, starting with the "Add in new text section", which is this section just here. We're going to allow the user to click on a button and add new sections just below. But this we first need a button which the user can click on. I'm going to place this over in the card edit. Just above this V for loop, placed in a new div with the class of add section wrapper. Based on a button and effects of add new text section. We don't want the user to go crazy and add lots and lots of different text sections which won't fit on this page. So what we're going to do is restrict the user to only add in four sections. Inside the function we will also take care of restricting this to four sections. But on the front-end inside of our template, we're only going to show this button if the number of sections is less than 4. We already have access to our selected page props which will have this information, grab the full page into the sections and we can check if the length is less than 4. If it is, we'll learn it display this button. We've got these clusters here, I'm going to grab this and go down to the "Style Section", which can also be scoped. Before we do this let's refresh the browser and see where this button is placed. Now this leaves us with an issue. We see the error of cannot read property length of undefined. If we take a step back and think about this, some of our pages don't have any sections at all, such as some of the back of the cards and some of the inside left don't have any text sections to display. Here we are trying to access the length property of something which doesn't exist. To deal with this, we can add in a question mark just after sections and this is what's called optional chaining. This is all JavaScript and nothing to do with VueJS. This optional chaining is basically a way of saying that sometimes these sections may be there and sometimes they may not be. We are completely aware of this so don't throw an error. If we save this and now refresh the browser, we see we don't see any issues even if we don't have any sections such as this back of the page. With this address, we can now go back to our style section and we're going to place this "Add new text section" over on the right-hand side. This will make use of the flexbox and then pushes over to the right with "Justify Content Flex End". See straight away, this is pushed over to the right of our section. This also needs some margin on the bottom. The space is out from the content blow. The content blow is currently the subject and we no longer have a use for this. We can go to the text inputs and also remove this from our template. This button and also the free enough links about the talk would also benefit from a hover state too. When the user hovers over this you can change the color so the user knows this is clickable. Since we want to apply this to different sections, I'm going to place this over in the [inaudible] view. Since this style section is not scoped, just blow the button. Hover states and also the same for any list items too. Just one property for this will define the color property. This RGB value of 108108241. Your hover state is now working as now we can push on with the functionality to add a new section. After this, we're going to go over to what we use current card and create a function which is going to update our selected card. Down to the bottom, create a new function called "Add Section." Remember before we said we're going to limit the number of sections to before. But we can also take care of this at the very top of our function. We can check if the selected page, the value, the sections, the length is greater or equal to 4. If this is true, will then return out of this function without running the lines of code, which we're about to write. What we're going to do now is to basically create a new object and then it pushes to our selected card to keep the same structure as all of the rest of the cards, head over to the data dot js, and copy of one of these section objects. But to all function stores on a constant called New section. This equal to objects. To prompt the user, we're going to set the user input to be "enter your text here". We can keep in place a default height, a default color, font family, and all of the rest of the options we can keep as a default value. You can also change these if you prefer to. Then what we need to now do is to select our full cards, go into the selected page and then add each new section. This is very similar to what we're doing when we updated the card just here. Just blow our new section, crop the selected card, the value into the pages and we can access the index number with our get page index function. Call this function into the sections. Then we're going to push a new item to this array. What we want to push is this new section. Return this function at the bottom and then back over to our code edit where we can import this file and also access our function is used to run card. This is from our composable folder. Then interviews current card. Next we need a setup function to accesses this function inside of a constant. The function was at section. This is from use current card and finally add section can be returned from our setup. [inaudible] any clicks can call this function that's our button, I click, which we'll call our add section function. Let's try this out, refresh. This particular page has three sections already. We try to add one more. That's all four sections and our button now disappears. Let's try the front. Have one section, 234 and this is all now working. The upcoming videos we'll follow a similar pattern over in the use current cards. We are going to be creating a series of functions to update our selected card and then call this function from our particular components. The next one will be to remove one of our sections.
120. Removing Sections: In the last video, we setup this button to add a new text section to any of our pages, and in this upcoming video, we're going to setup a button to remove any one of the sections too. Removing a section will be a little simpler than adding them since if we take a look at our data, inside of any of our pages, these sections are placed inside of an array. This particular one just has a single section of text, but we could have up to four sections inside of here. To remove them, we'll just need to know the index number of the section we want to remove. This, we are going to create a function in useCurrentCard. So let's go down to the bottom and create our function called removeSection. As we just mentioned, the removeSection will also need to take in the sectionIndex when we call this function. So just like above when we added a new section, we also need to grab the selected card, go into the current page, and then into the sections array. So we're going to move this over. But this time, rather than pushing a new item to the array, we want to make use of the JavaScript splice method. With splice, we need to pass in it two values. The first one is going to be the position inside of the array of the item that we want to remove, and this will be from the sectionIndex, separated by a comma, the number of items we want to remove. We just want to remove the single section. Okay. Then return this function. This function is going to be called from the text input component. Our composable has already been imported, so all we need to do is to import or extract our function, which is removeSection. Also, return is so we can use this inside of our template. RemoveSection, as we've just seen also, requires the sectionIndex when we call this function. This component already has this available inside of props. So now we can go up to the very top of our template and just above the text area, create a new section which is going to wrap our button to remove the section. For styling, place in the class, text-input-header. Then inside here we're going to place in a button. Alongside this remove button, we'll also add some of the other buttons later on too. But now, we just need the single button with an x, which is going to listen out for a click. Then trigger our function. Pass in the sectionIndex, which is our prop. Go to the browser and test this out. Click on the X. This is now removed. Let's go to a different card and remove the middle one. Since everything is reactive, removing these will also update the preview too. Also just as a side note before we wrap this video up, if we go into the console, we see we are currently being yelled at by the view library because we're using a watch function and this is just because of the way we've structured this watcher. If we go down, this watcher is expecting a callback function as the second value. So to remove this one, what we need to do is to remove this function directly. Create a new updateCard function below. Pass this in. Then we can simply call this update as a callback. Go into the browser. Open up the developer tools, into the console, and this now gets rid of our error message.
121. Rearranging The Order Of Sections: If we have multiple text sections just like we see here, over on the right-hand side, it may be useful to also place in a up and down button to rearrange the order of the sections. To do this, we're going to go up to the text inputs and then it need at the very top. In the last video, we created this section. This was for our header. Then just above this button, create a new div section, which will be a wrapper for both of this up and down buttons. The first one, we're going to list now for a click, which is going to trigger a function we haven't created yet, but this will be called updateSectionOrder. This function is going to take in two things. The first one is going to be the sectionIndex, which is from props, and the function we'll needed so it knows which section we want to move. The second value is the direction we want to move this section. So if we think about this, this is an array. So this will be index position 0, this will be one, and this will be two. So to move any of these sections up, we need to pass in a negative one to push us up in the array. Inside of our button, we'll place in a icon inside of the image element. The source is the at symbol into the assets, the icons, and we have a icon called arrow-up.svg, the alt text of arrow up, then it duplicate this button for the next one and place this just after. This will call the same function, but this time we will have a positive one, the arrow down, and the same for the alt text. Okay. There's our two buttons, but this also needs some styling too. This section will wraps all of the buttons as the classic text input header. I'm going to grab this and nest it inside. We have our div and also this button too. We can make use of the flexbox and also the space between property to align this across the page. Down to the bottom into the style section, I did our class. First, the display type of flex. This will give us the default flex direction of row, so this will be placed across the page. Just by content, we'll add the space in between and then some padding on the bottom. Next, reduce the size of the images by targeting the same wrapper and then the image elements. Width of 15 pixels and this will reduce the size of our icons. Some padding on the right of five pixels. Just play some space in between each one of these icons. Okay. Now we need to set this function up over in useCurrentCard. Down at the bottom, function, call this updateSectionOrder. This was also past the sectionIndex as the first value, and the second one was the direction, which was either a positive or a negative one. It is positive or negative one number which we'll use to update the sections index position. The first step is to create an array which is going to hold all of the sections for this page. Just like above, we can access our sections with this part of the code, copy this. Just below this before we actually perform this switch, we also need to check if this switch is actually possible. By this, we mean if we are updating the very top on here and we click on up, this has nowhere to go. The same for the bottom one too if we click on down, we don't have a section below this to replace it with. So to guard against this, we'll place in an if statement, paste in our sections. Then inside here, if we pass in our sectionIndex plus the direction, then we'll check if this is equal to undefined. Okay. So just to clarify what we're doing here, we are grabbing all of our sections on this page. We want to guard against this being either the first or the last section where what we're doing is is grabbing the current sectionIndex. So if this was the first one, it will be positioned zero. If the user clicked on the up button, this will be zero and then a negative one. If this was the case, this would equal undefined, so this would then be true. If sectionIndex, however, was our very last text area, in this case it will be index position 2. If the direction was a positive one, this would then be the index number 3, which is not available so therefore we would return undefined. If even one of these is the case, we want to return it back from this function without updating our sections. If not, the function will continue to run, and if we reach this stage, we can now switch all positions. This is really just a matter of switching the index positions of two array items. These two array items is the tax section we've clicked on and also the tax section which is in the place we want to move it to. This is how it looks. We need to create one array, which is equal to another array. We're going to set our currency rate to be equal to a different one. This first one, which is the current array, is going to take in two things. First, the array item we want to move, which we can access with array, then passing our sectionIndex, and then separated by a comma, select the array item we want to replace. So we're going to grab our array, but this time we'll pass in the sectionIndex plus the direction. Then we do the opposite in the second array, so we switch around the positions. The first finding is our array, pass in the sectionIndex plus the direction, and then replaces with array sectionIndex, and return this back from our function and then import this over in our text input. We're already accessing our useCurrentCard function, but just passing our updateSectionOrder. This name matches the function which are placed in at the very top. So when we click on any of these buttons, they should now update our index numbers. Let's try this out into any one of our sections. Now we can click down on the top section, it now moves into the middle. Click down again on the top section, and this one I'll swap the index numbers. Look on the bottom section and this works, and also test against our guards for the bottom section. If we click on down, nothing happens. If we click on loop on the top section, this also works fine too. This will also update the edit section and the preview section since everything is reactive, but we have one small problem though. If we go to any one of our pages which has no new sections, we add four new sections into here. This can be number 1, number 2, number 3, and number 4. Let's try to update this. If we click on the up button and down, we don't see anything being updated on this edit side. Now, this goes back to something we learned about earlier, which is adding keys to our v-for loop. When updating a list of items like this, v will need a unique key to be able to efficiently update. We have not yet placed an ID on our new sections. We can see this will go to the card edit and into our v-for loop. Each one of these keys is relying on a section ID. To generate an ID for this, go to our useCurrentCard and we can place in an ID field inside of our new section. How do we go about generating a unique ID? If we're using a database, this would be relatively straightforward. The database would generate one for us. We could then get back the saved item and also store this in our new section. For our case, we need to generate one inside the app. For this, we're going to import a new package, so open up the terminal. Host on this server with Command or Control C, and then we're going to install a new package with npm, I've installed, and the package name is UUID. This package is going to generate a unique ID for each one of the items. It's really simple to use. If we go to the top of this file, then we'll import this module. We're going to import version 4 as uuidv4 from our package, which is UUID. Now, we've imported our version 4 and we're going to be able to access it with this variable just here. Now, if we go to the other section and set this equal to our name which is uuidv4, and then call this function so we generate a new ID. Let's try this out. Now, we need to restart the server with npm run serve, open the serve and add four new sections; number 1, number 2, number 3, number 4. Let's click up, this will move up. Again, let's try down. This is now all working correctly.
122. Changing The Section Height: When we created the menu when we hover over this text area, we also added a few buttons to increase or decrease the height of the section. We can now use this and create the function to make this change. This is going to make each one of these text areas even smaller or larger. This [inaudible] to our menu, which is inside of the text input, and then I'm going to do a search for the menu middle. This is the section we need, just here, we have our up and down buttons. The button that needs to [inaudible] for a click, which is going to trigger a function when you haven't yet created called change height. This function is going to take in the number of pixels we want to increase or decrease the height by. I'm going to place it in at 100 though this button will increase the height of this section. Over this, the place is into our second button but this time we'll decrease the height by 100 down to the setup. Let's create this function. This is at the bottom. This was change height, which will accept the value and this will be either a positive or a negative 100 pixels. The first thing we want to do is to store the current height of the section or we can extract this from our current section. This is [inaudible] so we also need to access the value and then the height property. Just before we update this section of the high city section, we're going to pass in a guard. We want to guard against the section being either too small or too large. After this, we'll check if the section is going to be either reduced to a height of zero or increased above 600 pixels, which is the height of this card. To do this, place in the if statements, we're going to check if the current height we have just above is currently equal to 100 pixels and the value is equal to negative 100. So this is checking if the current height of the text area is 100 pixels and also if the user has clicked on the decrease button. This will result in a current height being zero. We obviously don't want this, so we need to check against this and also the opposite if we are at the maximum height. So if the current height is equal to 600 pixels, and also if the value which is passed to this function is equal to 100, if it is, we're going to return alter dysfunction without running any code. This has thrown an error and I think we just need to wrap all the section inside the brackets. But if we get to this stage below, this means we can now update the height. To update this, we're going to update this current section, we need to access the value, and then the height property, and we'll set this equal to our current height plus the value, which will [inaudible] 100 pixels higher or 100 pixels lower. This is actually all we need to do for this function because if we update in it the current section just here, we already have a watcher placed on this, which will then update our composable. This means we just need to return this back from all function, then we can test this out. For the first one, increase the size, let's get it bigger, and we can decrease the size too. If we keep clicking, we can't reduce this any lower than 100 pixels and also if we increase this, it gets to a maximum size too.
123. Additional Styling: This video is going to focus on finishing off some of the CSS touches which we need to add to our project, starting with the font over in the app.vue. Currently, we have this font that's just here, and you're free to use this one if you prefer, or any other font family. With the start project, if we go into the index.html, inside the header section, we have this link to two different font families. This Merriweather is the one I'm going to place inside the app.vue and we already used this overblown early on inside of the header. We can see this if we go into the app header. This is the font which we set for this section. But let's go back over to the app.view. Let's place in the Merriweather font, with a Serif fallback, just in case there's any issues loading this font. Also, we'll add this font to the input, and also the text area too. Just copy this. The reason we do this is because by default, the inputs do not inherit the fonts automatically. So we also need to set them just like this. Let's save this. This new font has now taken effect. Inside of this create view, which you can see just here, we also have some issues to resolve. We have these huge page buttons which need to line up across the top, and also shrink down the size too. We also have some spacing between each one of these text sections and this also results in the sections being pushed off the page, over to the text output component. We'll first deal with these text sections by removing any default margins and taking advantage of the box-sizing too. The start section is all wrapped in the p element. The default margin will set back to be zero. Save this and check it's working. Each one of these p sections is also surrounded in the border. Remember when we add a border to any HTML element, it will also add it to the overall size. To prevent this, we can set the box-sizing to be border-box. The next thing to do is to take out of these four buttons to switch the pages. This has its own component, which is the SwitchPage.vue. We'll have a pretty standard unordered list. We can make use of the CSS flexbox inside of the Style section. There's our links placed across the page because the default flex direction is row and I think we just need to move these links just outside the card section. We can see these have this gray background color. But we're just going to go to the Create section, which is in the vues. I think we'll just lift this switch page components to be outside the wrapper. This will be placed just above all of the cards sections. If I go over to the style in inside of the switch page component, continue to work with the unordered list by adding some margin on the top and bottom, of 1.6 grams, and then zero on the left and right. The next thing we need to do is to reduce the size of each one of these icons and also align the text to the center of this icon. Each one of these links had the class of page select, and we can use this class to reduce this icon size. First of all, the alignments will make use of the flexbox, and then set the vertical alignment with line items into the center. There's our vertical alignment, and next, some margin on the right-hand side of 10 pixels. This is going to give us some spacing on the right-hand side of each one of these links. Next, the image. The maximum width of this icon of this image is going to be 30 pixels. It should reduce the size of our icons down. Also, if we add some margin onto the left, this we'll add a little bit of space in between the image and also the text. Good. This project now looks a little bit better, and you are also free to go ahead and change any styles which you would like to just some base styles to improve the overall look and feel. Just before we wrap up this section, we're next going to look at using provide and inject with the composition API.
124. Provide & Inject With Composition: Provide and inject is something we've already looked at, the having a source of data in a top level component, and then directly injecting it into any child components without needing to pass down props to each component level. We can also use this with the composition tool. The setup function can accept either provide or inject. One of the issues earlier was the provided data was not reactive. When there was a change to the data inside this provider, any of the child components below which relied on this, was not updated. But now we know about view free, the composition API and also reactivity, we can now make use of this to fix this issue. Over to the update view components and I'm going to show you how we can use this with composition. Remember though a provider can be placed in any component, not just this main update view. First, we need a setup function with some data. Place this inside of our script section, and then for our demonstration purposes, create a user object with the name property and also the occupation. Since this is our top-level component, we're going to place in the provider inside of here. To do this, we need to import this from the view library. This is provide from the view library. Then down to the setup function we can place in our provider, which is going to take in two things. The first one is the name of this data, and this is like a variable name. Next one is the user. Then separated by comma we're going to pass down the actual data which you want to send. In our case, this refers to our user objects. Inside of any of these child components, when we inject in the user, this is the data which it will receive. To save this file, I'm going to go over to the text output. We already have a setup function and we also have an import just here, so we can also import inject into here, and then we can inject this user object down to the setup, create a constant. To store this, user data, access inject. Then when it passes in the data which you want to inject, and in our case, we give this name the user. Pass this in as a string. We can also add a optional second value two, which is a default value. Like when we use these slots early on when we added a default or a fallback piece of data, I'm just going to alternate string of no data available. Return is user data constant down at the bottom. We can check this is working by outputting this inside of our template, over to the browser and reload this, and there's our object at the top of each one of our text output components. Now, reactivity was an issue early on when we looked at provide and inject, with the options API, let's see how this compares with the composition. Back to our app dot vue, we can simulate the data obtained with set timeout as NA function, which we'll call after three seconds. What I want to do inside of here is to start to use objects, access the name property, and set this to any different value. We can try this out with a refresh. Give this three seconds. We see no updates inside of this child component. Even with composition provides and inject is not reactive by default. But as we already know, composition gives us access to reactive wrappers, so we can use either rough or reactive. In this occasion I'm going to go for reactive, inside the app dot vue and pull this. This is now going to be a wrapper for our use objects. Cut out the contents of the object, place in reactive, paste this inside of the brackets. Save this once again and over to the browser, refresh. Give it three seconds, and our name is now updated. If we had multiple pieces of data to pass down to child components, we could also set up multiple providers, just like this one here, or alternatively, we could pass down all of our data in one big reactive object, and then pass this to the provider. This is how provide and inject works with composition. We don't need this for our projects, so I'm going to remove the setup function, the imports, we also have the inject inside of the text output, move the user data, the import, the user data, and then finally, our returned object. The use cases for provide and inject may not be as common now, since we also have the ability to use composable files and then import them directly into child components. But it isn't an option and a novel way of passing down our data, especially if we maybe have a smaller app, we don't plan on breaking up our code into smaller composable files. We've pushed through quite a bit of project code in this section, and our project is almost fully functional. In the upcoming section, we will discover that some more cool features of vue.js And also the view router provides.
125. Section Intro- Navigation Guards & Lazy Loading: Sometimes in our project, we don't want the user to visit every location we have or we sometimes also want to provide some checks to see if the user is actually allowed on any given route. In this upcoming section, we'll take a look at navigation guards, which is provided with the Vue router. We'll also look at a never really useful technique called lazy loading, which will allow us to split up our application into smaller pieces to improve the page load time.
126. Navigation Guards: As part of the view router package we've installed, we can make use of navigation guards when changing between our route. You can think of a nav guard as somebody standing on the door of a club, asking to see some identification before you can get in. In terms of code, this identification, it can be anything such as if the user is logged in, if they have the correct permissions or even things like only allowing a user to access a download area if they have purchased that particular item. We can basically run any code which we like either before or after a change of route. There is different guards available, but let's take a look at some of the common ones, starting with if we want to apply a guard against all of our route, with this we can use a global guard which is called beforeEach. We do this over to our route file, and this is inside of the router and then the index.js. Below our route's array, we can access our router with this router constants. We need to go just below this, we can then access this router and then call the guard, which is beforeEach. BeforeEach is a global navigation guard so it will apply to all routes in our project. When is a change, we're going to provide a callback function, and it's inside this function where we can intercept this route change and then it runs some code. As a very basic example, we can return false inside this function and this will stop any change of route occurring. We can't even load our app. We can't click on the Home button and also these category links don't work either. But it's at its most basic. This is how we block a change of route and since this is not very specific it will apply to all route changes. BeforeEach also gives us access to the route details, where you're going from and also the route we are leaving to. We're going to pass these into our functions with to and from. As it sounds, the to value is route where you're going to and the from is the one which we are leaving. Let's see how these look. Passing the to, and then also the from variables. Over to the console. Those are our two objects. The first one is the route where you're going to and this is just the home route. This makes sense. The second one is the from objects, and since we are not changing routes, this is still default slash versus if we remove the return statement and to switch between its about routes. We go from the homepage to our category, and we can see this category if we click on to section, and we covenant from the home route. These objects give us access to things such as the full path of the route, the queries, the name of our routes, the params, and these are all fully accessible from our function, and this information means we can be more specific with which routes we wants to guard. For example, instead of watching all of our routes inside of here, we could watch for a user entering a admin route. We can add an if statement inside of here and we can access our to value and then select our name. This name, such as the category just here, is a name which we give to the route inside of our array just above. We can access the admin. If to.name is equal to admin, and make sure this much is the name from above. If this is true, we're going to return an alert to the user with the text of "This area is for logged in users only." Let's check this out in the browser. We don't use any from variables, so we can also remove this. All of the route seemed to work completely fine, and let's go to /Admin, and there's our alert. Once we click on "Okay", we are still taken to this admin route. Currently we're not actually block in the user, we're just simply displaying an alert. In reality though we maybe would want to check if the user is logged in or has admin privileges, rather than it just an alert. If there were not allowed access, we could either return false or even return a router redirect method, such as the push method, and the way to do this is to also access the relative variable, and then we still have access to do things such as the push method which we looked at earlier. This means if the user is trying to access this admin route, we could then redirect them to the homepage. We're on the homepage at the moment. But if we go to the admin, This will then push us back to the homepage. Along with this before each guard which is run before the route change. We also have afterEach to, and this is a hook which runs once the navigation has finished. Since the navigation has already happened, it cannot make any changes such as redirecting or blocking the route either. Or instead, we can maybe use it for things such as tracking a user's visits to a particular page or even changing some page details after it loads. It can also take in at the to and from data if needed too and both of these guards are considered global since it will run for each navigation change. If we didn't want them to be global, we can also place them directly into a single route too. I'm just going to comment this out and leave this in for a reference. But we can also place this into any one of our component routes too. Meaning if we only wanted to protect this admin route for example, we could instead directly place a guard on this object. The guard for this is called before enter and then we setup a method which is going to run each time we try to enter this admin route. Just like before we can do things such as return a false to block any access to this route. The homepage works, the category works, the crate works. But if we're trying to go into the admin, we can see the component is not loaded since we don't have the admin title. This could also take in the to and also the from variables too if we needed access to this information. But realistically, we would again do something like checking if the user is logged in before we actually load this route. We don't currently have access to a user for this project, but we can simulate one for this example. Just above the routes that would be constants called logged in, which will be initially false. We can then use this down inside of our guard. Instead of returning false directly, we're going to first check if the user is not logged in before blocking this route. Remember this is currently false. So if we try to refresh, we don't see any access to this components. If we change this to true, we now have access to our admin. This type of guard will also only trigger on a full route change. Meaning, the change of route must trigger a new components. It won't run if we just have a change of the query or the change of params in the URL. Just like earlier when we had to alter these manually, almost switching between these categories. Well, for this example, we still staying on the category components, but we are just changing the params. For this particular case, the guard will not run since we're on the same components. Another useful thing we can do with these guards is to move the code into stand alone functions, so if I had a function which checks if the user is logged in. We could then extract our code from our guard. It will put out the if-statements, place this into our function, and it's function name can be now fast forward guard as an array. So for this, we don't want to run this method, instead pass in an array an old function name. Save this and our routes will work. Let's try to visit the admin. We can access this since our use is true, changes to the false, and this time it blocks our access. If needed, these functions can also take in these two and this from data, if we need to access the route information. A really good use case for these functions is if we have multiple guards on different routes, and we'll want to check if the user is logged in on more than one. This means rather than it duplicates this code inside the function for each one of these routes, we could instead reuse these function as many times as we need to. The last type of guard I want to show you is in component guards. Just as they sound, they go into the individual component files, such as our admin component. So before we do this, let's comment this out, also all guard from these component, and then also our logged in variable. For these in component guards we'll also stick with the admin component. So head over to this page inside the views. Write our script section. The first guard we're going to look at is called beforeRouteLeave. As it sounds this is going to run this function before we tried to leave a particular route. This may be useful for prompting the user to log out before they leave this particular page. Also, make sure they have saved any changes before they are lost. At the prompt, the user we're going to access the window, they confirm, and this will setup a confirm message to the user. This message will be a string and I'm going to say, "Do you really want to leave?" You have unsaved changes. It just needs to be a lowercase w, and then will store this inside of a constant called confirm. This confirm box is going to give the user the option to either click on okay or to cancel, which will leave this constant either true or false. If this is false, we are going to cancel the navigation by returning false. That seems to be working, refresh the admin page. Now if we try to click on a different route, we see the confirm. Click on "Cancel" and this will be set to be false, so we'll return false, and this results in staying on this admin page. However, if we try to make a change and click on "Okay", we then confirm this is okay to do, and then we'll go ahead with our change of route. This also has access to to and from if we need to access the route's information. We also have access to two more guards which we can use; one which is for entering the route, and one went for updating it. The first one is beforeRouteEnter, and see this we'll place in a console log. This one is enter and a comma. The second one is beforeRouterUpdate, placed in a console log of updates. We can now see when each one of these routes are run. Save this and open up the developer tools. Over to our admin. We only see this enter message, and even if you keep on refreshing, we don't see the update message is run. So what exactly is going to trigger this update message? Well, this one runs when the route updates but the same component is reused. So as an example, if we click on any of these links over the top, it will confirm this. When we switch in between all three of these links, we're staying on this same category components, but just updating the query. For this use case, this beforeRouteUpdate will run. Confirm this from [inaudible] both of these knob guards, and then go over to our category page. This is inside the views and paste this into our export. Refresh. We see enters, since we've entered the catalytic component. Switch between these three pages, and you see the update is now being called. Component guards can also be run inside of the setup function too but with a small adjustment. First, inside the category, remove these two guards, and then go back to our admin components. By using composition, we first need to import them from the view router package. With composition, we have access to beforeRouteLeave and also beforeRouteUpdate. The only difference is we use the on prefix. So this one will be on beforeRouteLeave. This is from the view router, commonly section L so it doesn't run, placed in the setup function. Access on beforeRouteLeave, which is then going to run a function. I'm just going to make use of the same example from above. Paste this in and uncomment this out. Save this file and head over to our admin. Try to leave, cancel and we stay on the same page. Click on "Okay", and then the navigation is completed. These guards are really useful for so many reasons such as protecting unauthorized access, redirecting, and also write through to just display in a simple message to the user.
127. Lazy Loading Routes: Another feature that Vue Router offers is Lazy Loading. This goes back to the JavaScript bundle we discussed earlier. Our app is broken down into smaller components and files, which makes it a lot easier for us the developer to write and maintain our code. Tools like webpack, will embundle all of our components and files together into one big JavaScript file, which can be then read by the browser. As you can imagine on large projects, these bundles can become really big and slow down the page load time on the first visit. At the moment, if we go over to our routers index.js file, we import in all of the components we may ever need. So when users who visit our site may never click on any of these links or pages, but will still pay the price with downloading them in the initial bundle. Lazy loading is the process of splitting up these router's components into separate bundles or chunks, and only downloaded when needed resulting in the initial bundle size being reduced. Of course, there will be a small download time to pull in the new components when a route is visited. But if you're using components efficiently, the effect should be minimal. At the moment, all of these components are imported using the standard import syntax. With the add lazy loading, we can make use of a dynamic import syntax, which is supported by the Vue Router, and we do this by replacing each import with a function. Just before we do this though, let's open up the developer tools inside the browser, and we can see how things will look before we make this change. Go into the Network tab. The first thing we're going to do is to click on "Disable cache", so we don't see any saved files. For this example, we only care about C and our JavaScript files. So we can click on the "JavaScript" button to only display the JavaScript files. Mine is already set to JavaScript, but you may have this all link selected. With this selected, refresh the page and we now see we have downloaded two separate JavaScript files. The first one of the app.js is our main JavaScript bundle and the vendors bundle contains code from over third-party modules, such as the MPM packages we place in our project. The key thing to remember here is we only have one main project bundle, which is the app.js. Back over to our router file, we can now replace these static import with dynamic ones. First, comment this out, and then we're going to create a function for each one of these to replace our standard imports. The first one is the admin, and then set this equal to a function, and the way this works is all of these original imports will be imported immediately. However, a dynamic import is placed inside of a function, and this function will only be called when required. Rather than calling our import directly, we store this inside of a constant, grab the file path, and then paste this in as a string. We could also replace this, the @ symbol, and it should work exactly the same. I've duplicate this five more times and the second one is for the AppHome, paste the path. The third one is for Create. Next, we have the category, and then the last one is NotFound. Save this almost see what effect this has. If we refresh the browser, we still see our original two files, which is our bundle and the vendor's bundle but would now have renewed JavaScript files. Now, it can be hard to know exactly what each one of these files is, since they are just a number. If we click on these and go into the preview, we just see a lot of JavaScript code. We can also replace this number with a chunk name. Back to our router. The way we do this is to add a comment. We do this just inside of the import statements, and this is structured just like a regular CSS comments. The comment we need to place inside of here is the webpackChunkName, and then we give this chunk a name of our choice. Let's keep this consistent and go for the admin, and it fixes. Then I'm going to copy this and replace the rest of our import. The second one is for the AppHome, Create, the category, and then finally the NotFound. Now, this webpackChunkName's in place. If we go to the browser, we see that our names are now placed instead of these numbered files. This bottom one which is the AppHome should not be a surprise since we are currently on the AppHome page. But above this, we see some different files. We see the category and also the Create.js. Now, this may seem strange because we have not visited these page just yet. You may expect that, for example, the category is not loaded until we click on one of these links. Well, this happens because by default, when using the Vue CLI, it will make use of a technique called prefetching, which is like view anticipating what files we may need next. Since we already own this AppHome components, Vue then recognizes that this page we'll link to our category and then the create components. Effectively anticipates we will need these components sometime soon. The downloading of these two files will not start until our initial page is finished loading, so we won't affect the page load time. If we look closely, we also don't see the admin page or the NotFound components too and this is because we don't have any direct links to these two components from this AppHome. If we wanted to see this, we will need to go to the admin, which will then download the admin.js. Also, if we go to any unrecognized route, we see the NotFound component is now being downloaded. Both this admin in this NotFound doesn't link to any of the components, so we don't see any prefetching for this page. This Lazy Loading is a really efficient way of loads in our components and improving the initial page load time. Another useful feature is our components only downloaded wants to. For example, this NotFound components will only be downloaded from the server the first time it's needed, and then any future visits to this page will then serve up a cached version for efficiency so we don't download a file multiple times. This is how we can use Lazy Loading in our Vue Router. Next, we want to take a look at how we can group together related chunks.
128. Grouping Routes Into Chunks: In the previous video, we gave each one of these dynamic import a name by putting in a comment. Each one of these import is called a chunk, and along with using this comment to name our imported files, we can also use it to group together chunks to. An example may be if we had a navbar, we could group together all of the pages in navbar links too and download them all together. The way to do this is to give each component we want to group the same chunk name. Currently, even though we prefetching the chunks, the category on the create components still download it separately. If we wanted to group them, all you need to do is to give these the same group name. For example, a card-group. The same for the category. Then back over to your network tab and refresh. We see just like before, we have the Apphome which is lazy loaded, but above this, we no longer see our separate imports or create and also our category. Instead we have this card-group which contains the contents of our two chunks. The use is for grouping together chunks like this will vary depending on the structure of your project, but this can be really useful for grouping together components which you needed at the same time. This technique really gives us a lot of flexibility to control which components are downloaded at any given time.
129. Section Intro- Uploading Images: Hey, welcome to this images section. In these upcoming videos, we're going to look at how we can allow the user to upload a new image to any one of our four card pages. This image will be a background just like the ones we've seen on the sample cards. These images will be uploaded, we'll read the file contents, we'll save them to Cloudinary, we'll update our card preview, and alongside all of this we'll also look at how we can remove existing images and also reposition them too. I'll see you in there.
130. Image Upload Component: This upcoming section will be focused on the card images. Each page, we place an image components over on the edit side and this is going to sit just above our text input components. We'll have this at the top, and this will mean we can upload a new image to any one of these pages. If we already have an image just like we have on the front cover here, we can also replace this image too. This image of low component is going to send the image of the Cloudinary. We're also going to display a little preview image once we selected this, and also this will be updated in the card Vue 2. To get going, we need a component of this to the card folder and then we'll call this the image upload. Setup the templates. This whole component will be surrounded in the div with the class of image upload wrapper. Inside this div, a level forehead in with the text of image, and then with that, we're using all the recommended image size. We'll keep to be 450 pixels by 600 pixels so this fits nicely on the card. Next, an input and the type for this input needs to be filed so the user can select any image file from their own computer and image elements. But now this is going to be empty, we don't need a source. The source will be set very soon from the file which the user uploads. This is going to be a small preview image, so it will give us the class of preview image, a div with the class of image patterns, this is a wrapper for a series of buttons which we'll go into how to reposition the images to the top, the middle, and the bottom of the card, and also a button to remove these two. The top, the middle, and the bottom buttons are going to be grouped into a div right in the first button and each one of these buttons is going to have an icon. So we can go into the assets, into the icons folder. This one is the top.svg, the whole text of image top selector, and then we'll do this two more times. The icon for this, again in the assets, into the icons. This one is the middle.svg. The final button is for the bottom. The icon for this is the bottom.svg, the last alt text is the image bottom. All three of these buttons are grouped together inside this div. If we go just below this, we'll add one fourth and final button which will be to remove the selected image. The class of remove_btn. I want to set this up to trigger a function later on in this section. But now, this is all of the templates action we need to display on the screen and we want this image upload component to display at the top of this edit section. So go over to the CardEdit.vue, import our component with the name of image upload. The file path is into the components, this was stored in the card folder, and select our image upload, place this inside of the components object, and then render this at the top of our template. Over to the browser, there's our image, upload components, and they should display for each one of all pages. This of course also needs some styling too. We need to align all of this content and also reduce the size of these buttons too. We also want this to blend in with the text inputs just below so that we'll add some common shared styles to both of these sections. The image upload that we just created have this class of image upload wrapper, and the text inputs which are just below has this class of text input wrapper. We can now apply some shared styles over in the app.vue to both of these sections. So down to the bottom of the style section, the first one is the image upload wrapper, and then also select the text input wrapper too. So for this, we're going to place some padding inside of the element, some spacing on the bottom of each one o0f these elements with a margin, a box shadow, and we'll also round the corners too. First, 10 pixels apart into all four sides. So margin, which is only going to apply to the bottom of 10 pixels, a box shadow of one pixel, one pixel, two, two. The color which is DDD, which will be a gray color, and I want this box shadow to be on the inside so we'll set this to be inset. This gives us this effect of the one pixel shadow on the right and also the bottom and this also blends to two pixels on the top on the left. We'll run these corners off with the border radius. Just a small one will be fine, all three pixels. This now gives us the effect that these are all separate sections. Now, we're going to go back over to our image upload component and apply some styles which now only apply to this image of low component. So the style which is also going to be scoped. First of all, push all the content over to the left by selecting the wrapper, which was image upload wrapper. Extra line off left,and then next, we're going to add some space in between all three of these buttons and also reduce the icon size down to, we'll go up to the template. All of these four buttons are surrounded in this image buttons class. So if we set this Cluster B, a display type of flex, this group of buttons will appear alongside our Remove button. The class of image buttons, the display type of flex, and then spaces out with justify content and place the space between, as our button now alongside all three of these icons. Next, we'll reduce down the size of all three of these images. These were in the same wrapper which is the image buttons, but we'll only apply this to the images. The width of 30 pixels. This is now a little bit smaller onto the level four heading which is the image text. The font weight of 300, and we'll just reduce down the size of this so we can see this better. This will just give us the lighter text. Also some margin of 10 pixels on the top and bottom, and zero on the left and right. Moving down, we also need some space between this file input and our free images. So select our input and then we can place in the type which is equal to file, and the styling will only apply to any file inputs. Paste in some margin of zero on the top, zero on the right, 10 pixels on the bottom, and then zero on the left, then some space between these buttons, grab all of our button elements, and then some margin on the right of five pixels. Good. The next thing we're going to do is to change the color of this Remove button and also the hover color to at the bottom of all styles. These are the class of remove_btn. The only property we need to add inside of here is the color, add an RGB value of 208, 90, and 90 for the blue, which gives us the red color we see here. Then finally, the hover state. So I'm going to duplicate this, but this time, target the hover state and amend the RGB color to be 123, 44, and 44, which is just a slightly darker red color which we can see if we hover over this button. That's it now for the visuals of our image upload component. Next we need somewhere to actually store these images which the user uploads. For this, we're going to take a look at a service called Cloudinary.
131. Setting Up Cloudinary: We now need a safe place to upload and store our images. A popular service I use for many of my own site is Cloudinary, which you can find here at cloudinary.com. Cloudinary is a hosting service for videos and images, and it also has lots of great features to such as the ability to transform our images and also serving the correct size images, and also formats for the user device. I've already got an account says about Cloudinary, but if you've never used this, go ahead and create a free account. It's free to sign up, and also gives us a huge free allowance of about 20,000 free image uploads. Once you're signed in, you'll be taken to the dashboard area, which will look something like this. Well, before we upload any images to Cloudinary, we first need to setup a preset. A preset is a set of defaults with images which we upload to transform the size and the format. We can set this from our settings. The settings can be accessed from this button just here, and then go into the upload section. Go down. In here, we're going to set up and upload preset. At the very top, we have this upload preset name, which is going to be important. I'm going to copy this with Command or Control C, and then go to our image, upload components. Let's just create a script section for now, and then place this in at the very top as a comment. The first option we have is the Signing Mode. Have the option of Signed and also Unsigned uploads. The Signed option involves using a web server, design the images with our Cloudinary secret, which we seen just before in the dashboard. Or we have the Unsigned version, which we're going to use, which means, we can directly upload from the browser without needing the secret. If we wanted to, we can also create a dedicated folder where we are going to place in these images. Let's go for the cards. The rest of it, we're going to leave us default, and then head over to the media analysis section. This section is if we want to upload our images with any tags. If we want to add any kind of AI such as face detection, if we want to extract any text from the images, and lots more advanced features, which we don't currently need. Below this, we have the image manipulations. This will give us the chance to set up any default image sizes and also formats, and even add some rotation or corner radius to as we upload this image. Let's leave all these as default, and then save these presets. Make sure you've taken note of this preset name and added this to your project. Next, we will take our first steps towards uploading this image by reading the contents of the file uploader.
132. Reading File Objects & Previewing: Next made a function to upload this image. We could add this two-way composable file if we wanted. But since we only need this in this image upload component, we want to place this inside of here. So we have a script which we added in the last section, and place in a setup function, so we can add some composition code. The function we need will be called Upload File, and this will also need to take in the event details too. This event details will be passed when we upload an image from this image uploader. We don't need to pass this manually. This will be automatically received from our input, and we can access this inside of our function. So let's also return this, and we can now call this function up in our file input. To do this, we'll list now for a change, and then run our function. This change will be run once that image has been uploaded. Back down to our function, we can see what information is included with this event by doing a console log. Save this file and over to the browser into the console. Now from here to see this run, we need to upload an image. You can use new images if you prefer. Good to have a list of all of the images which are used in this course. So I'm going to select one of these, which will enter a function and also our console log if we open this up, head into the target, and then into the files. From here, we only upload in one image at a time. So our image will always be this first file in this array at index position zero. We see things such as the file name, the file size, and also that this is a PNG image. So we have the information we need, but next we need a way to actually read this file data and for this, JavaScript has what is called a file reader object. So below our console log. Let's obey constant to store this in cold reader. This is equal to a new JavaScript file reader object. File read object is able to read the contents of the file from the user's computer. This image can be used to set a small preview image, which was set earlier up in the template. This is the empty image element which currently doesn't have a source. To set the source, we're going to set up some reactive data. This will import the reactive package from view, and then inside of the setup function, set up a constant call state, which will contain all of the data which we need for this image. We'll also wrap this in the reactive wrapper and place in an object. The first property we are going to add inside here is for the image preview. Currently, this is just going to be an empty string, but we're going to update this from our file reader. Once we do this, this will be set to the image source inside of the template. So we also need to return our state. Pass this into our object, and now we'll combine this up inside of the image elements. Place in the source, and this is going to be equal to our state object, and then the image preview property. Currently, of course, this won't work, since this is set to be an empty string. So we need to update this with the contents of our file reader. The file read has various properties and methods we can access to now help with this. But before updating the image preview, we need to first be sure that the file has finished reading. So if we have a really large image which has been uploaded, this reading stage may take some time, and so you check this. First we'll access our reader, and then use the onload property. This means once the file reader has finished reading our image object, this is then going to trigger a function and it's inside of this function where we want to update our state dot image preview to be equal to the reader dot results. This will run once the reading operation has finished, but we have not actually started to read the file contents yet. All we've done is create a new empty file reader object. To actually read the file, we have a method available called read as data URL. So select our reader, goal, read as data URL, and the file data we want to read is what we've just seen inside of the console. So this was inside of the event, inside of the target. The files which was an array, and we always only upload in one single file. So this will always be position zero. Okay. So once this has finished reading the file, this will then run our onload function, which will then update our image preview. Let's check this out. Refresh the browser and select any one of these images. Open this up. We can see our image preview is now working, and you could also restrict the size of this image, too. Particularly if we have a tall image like this one. It would also be a good idea to handle any potential reading problems, too, and send a new message to the user. This we can place a message into our state, and initial empty value, and just before the onload property, we can also handle any errors with on error. So reader.onerror. This property is also going to trigger a function, it's just like above, where we will update our state.message to be equal to a new string. This message is up to us, but placing something descriptive, such as, there was a problem uploading your message, please try again. Okay, we don't need to return anything since we already returned our full state object. All we need to do is to place in a text element, which I'll put as the state.message and also a conditional too to only display this message if the message exists. Good, this will now let the user know if there was an error when uploading the image. Now we know this image reader is working correctly. Next, we'll use it to save our images to Cloudinary.
133. Uploading The Image File: First video gave us access to the user's uploaded image. We then started image preview for the user to see. Now we're going to save this to Cloudinary. Once Cloudinary receives these images, we'll also assign it a unique URL too. We'll also soon save this URL to our card object. We're going to do this inside of our upload file function, just below where we read the actual file. Since we're sending data to a different location, we can handle this using try and catch, which will handle any errors. Then catch which will take in any errors. If there is an error, we'll throw a new error, passing in this error message. Up to the try section, the first thing we'll do inside here is to create a new FormData object. A FormData object is a way of constructing sets of key value pairs in a correct format which is needed to send it to a server. These key value pairs will contain our Cloudinary presets, and also the image file we want to send. We'll also keep this in sync with our state, so just above where we have our state object. Also the formData, which will initially be null. We can then update our formData with the contents of this object. So this will be state.formData. Set this equal to our object which is currently empty. But to add some contents to this formData or some key value pairs, we can use the append method, which will look like this. We gain access of our state.formData. Then we can call append to passing our key value pairs. One of the things we need to upload with this image is the upload_preset. This is the key of the name, and then separated by comma the value is the preset, which we receive from Cloudinary. Wrap this, and place this in as a string. Duplicate this and the second key value pair is going to be for our image file. The file is available from event.target. Its FormData is now going to be used to send the Cloudinary. But we first need a way to send this request. For this, I'm going to use a package called axios. Axios is a package to make web requests, and we can install this using npm. Open up the terminal, close this down with control C, and then install this in our project with npm. Install all the i short hand, and then the package name which is axios. Once this is done, restart the server with npm run serve. Back to our component, and to use this axios package, we first need to import it. So import axios. Since this is a node module, we don't need to add in the file path. We can just add the package name. Back to our function, axios, now we need some certain things to make this request and send this data to Cloudinary. I'm going to store all this inside of an object. So const imageRequestData. Setup this object where we are going to pass in three things. First, the URL, the method, and also the data. First of all, we have the URL, and this is the location where we want to send this image to, and this is the Cloudinary API, pass this in as a string, which is https://api.cloudinary.com/, the API version number, which is v1_1. Then we need to point to our own account, back over to the Cloudinary dashboard. We can get this from our account details. This is the Cloud name which is at the very top. Copy this and this onto the end of our URL, and then /upload. By this we have the method, and this is a post request since we are posting data. Then last of all, the data, which is the actual data we want to send. We already have this stored in our state.formData. Good. This is everything we now need to make a axios request. So we'll call the axios library, and then pass in this image request data. If you wanted to, you could pass an object directly into here, rather than creating a separate object. But personally, I think this is a cleaner approach. Would just cut this typo before we go any further? Okay, say this. Now back over to our project, refresh this. Now if we upload a new file, use any image opens up. There's our preview over to Cloudinary and into the Media Library. Remember when we set up the preset, we also set up these cards folder. So go into this. There we see our image, and now we know this is working. In the next video, we're going to grab our images URL, which is been assigned to this image by Cloudinary, and then setup a update function to save this to our card object.
134. Saving The Image URL To Our Card: Previously we've made an axios request to save our images to Cloudinary. This image or this image's URL will also need to be saved to our card object too. But how do we get this image into our card object? Well, we can pass in a URL reference, which is provided by Cloudinary. When we see our cards just here, we have this copy URL section. If we copy this, paste this into a new tab, and this is a direct link to our image. This is provided for any images which you upload to Cloudinary, but we don't need to manually copy all of the URLs like this. Instead, when we make this post request with axios, we get back a response which we can then store inside of an object and access this data, hence we don't know how long it'll take to save this image and get back a response. We can also make use of async/await. Async/await will allow us to wait on the data coming back from the server before we save this into our constants, this will stop us from trying to access the data saved inside here, before it's actually returned. We'll await this data coming back, and so we can use this keyword. We also need to mark our function as async. We can log our response back to the console, and see what data is returned into our project. Upload a new file, since we're sending this to the server and it needs to be returned back, we may need to wait a few seconds for this to happen. Once this is saved and we get back a response, we see our console log just here. The data we need is inside of this data section, and this will then contain our URL, and it's this URL which we need to save to our card object. We've not yet created a function to perform this update, but we can place this inside of our component for now, remove the console log, and this function will be called updateImage. Passing the URL which is stored inside of the response, the data, and then the URL, this function is going to be placed inside of our useCurrentCard composable along with the other update functions. We also need to import this for the top. Import the useCurrentCard from this file path, which is the composables, and then into useCurrentCard. Before we can call this function, which we're doing just here, we also need to extract this from our file into the setup. Save it as a constant, which will be called updateImage, from our useCurrentCard function over to the useCurrentCard file, we'll now need to setup this updateImage. Now at the bottom, write this function and to update the card, this needs to take in the URL, which we're passing when we call this function just here. Now to update this card, we'll use a similar technique which we've used in the update function just above. We grab the selected card and then the current page. I'll copy this, at this interval of function. We don't need to go into the sections because if we take a look at our card directly inside of our pages, is where we have the background image stored, meaning we just need to update this background property, so like this. I set this equal to the URL which is passed to this function, return this back from our function, and now we can test this out over in the browser. What we want to see here is, now when we select a new file, we see the preview, it is saved to Cloudinary, and this should also update our current preview too, since this is kept up to date with our reactive data, let's use a file. Our function is triggered and our card preview is now updated with this new image URL. These images can also be placed on different pages too. If we select a different image for this page, this will be completely independent to all of the other pages. Good. Now when all this is working. In the next video, we're going to focus on these three alignment buttons to align this image, the top, the middle, or the bottom of this card.
135. Repositioning The Image: Inside of our imageUpload component earlier on when we setup the template, we placed in three buttons to reposition the image, the top, the middle, or the bottom of the selected page. For these buttons to work, we need to [inaudible] click on any of these and then run a function. The function we haven't set up yet, but this will be called repositionImage, which will take in either the top, the middle, or the bottom. Let's grab this, copy this, and place this into our second button, as in this string of middle, the third one is called the bottom. Over to the useCurrentCard composable, we can now set this up down on the bottom. This was repositionImage, as in the position. This will be very similar to our function just above. But this time rather than updating our background, we're going to update the backgroundPosition and set this equal to our position, which is passed to all function. Also, if we wanted to just above in this updateImage function, we could also pass this into and setup a default position for this image, such as the center and then return our function. So this back over to the image-upload, we're already importing our composable. We also need the function which was repositionImage, and then return this function back from our setup. This matches the function name which we are calling each time we click on these three buttons. Let's try this out. Currently, for correctly sized images which we have just here, the buttons won't work for this particular case because we have a full sized image. But if we go to choose "File" and insert a small image, which doesn't cover the full height of the card, we can now try all these buttons. We see straight away that the position is in the center, at the top, and the middle one isn't working. This just needs to be center. Let's try this. We have to reupload the file. We try this again. [inaudible] the top, the center, and also the bottom, and this fixes our issue. The reason this works because over in the cardPreview components, we set this up as a style property. Open this up and in the template early on when we set the background, we also added the backgroundPosition property and since everything is reactive, this is also being updated with the new position each time the data changes.
136. Removing Images: The last thing to do with our images is to remove them from this card. This shouldn't be too much trouble. All we need to do is to set it by function, which is going to select the background and set this to be an empty string. As with the rest of the update, head over to the useCurrentCard where we can setup a function. RemoveImage. This will be very similar to the updateImage, so we can copy this line of code, paste this in. But this time instead of setting the URL, this is going to be an empty string. That's all we need to do. Return this function. Then we can place this in the imageUpload component, save this as a constant. With this function, we could also return this part from our setup and call this directly from our remove button. But instead what we're going to do is set up a new function called remove and this is because not only do we want to call this function, we will also need to clear the image preview too. Set this up. The function called remove. First we'll access our state and the imagePreview and reset this to be an empty string. This will remove the smaller preview image we see inside of the edit screen. Then we can call our imported function. Return this back from our setup. We already have a remove_btn setup inside the template. We can [inaudible] @click, which is going to call this function. I'm going to give this a try over in the browser. Click on "remove." Our image is gone. We'll try and upload an image. Click on "remove." This will still delete the image, leaving our image functionality all now complete.
137. Deployment To Netlify: The first step towards deploying our application to the web is to build our project for our production. We can create this bundle for production by using the build command, which you can see if we go to the package.json, inside the sidebar. Open this up, and so far, to run our project, we've been running NPM run served, which will start with our development server, and it will also give us things such as error messages during development, but we don't want to see things like this during production. Instead, what we want to do is to build a production ready version of our application, which is a JavaScript bundle. We can do this with the command just below, which is build, and we, again, run this from the Terminal. What we need to do is to press "Control" and "C" to close down the Terminal, and this time, MPM run build, and this will start off building our project and all of the files will be placed inside of a disk folder. We'll see this in just a second when this finishes. We can now say this about the top of our sidebar. This disc folder contains a production ready version of our app, containing all of our CSS, our images, and also our JavaScript bundles too. For JavaScript, we open this up, contains our bundle which we've talked about previously, or multiple bundles if we're using lazy loading, just as we are here. So we'll see things like our admin bundles, our app bundles, our app home, our card groups which we setup for the chunk groups. We see the not found page, and these are all lead JavaScript bundle files which could be downloaded individually or together from the browser. These files are also minified and optimized for production, and we also remove things such as the error messages and any warnings which we see during development. We can now take this disk folder and use this to deploy to a hosting service. The hosting service I'm going to show you is going to be over at netlify.com. There are lot of great hosting providers out there, but this is really popular and an easy one to get started with, and there's also a generous free version too. To make use of this hosting, I'm going to open up the terminal and make use of the Netlify CLI. If you've not used the Netlify CLI in the past, we need to install this globally. We do this with MPM, install. This is the Netlify_CLI, and then we need to use the -g flag to install this globally. I already have this installed, so I'm not going to hit Enter, and if you do see any permissions, errors, where a setting this off, you may also need to either fix these permissions or use the pseudoprefix to install this as the administrator. Once we have access to this, when installed, we can then use various Netlify commands, and the one which we want to use is the deploy command. Make sure you're inside of the project folder, hit "Enter," and if this is your first time using Netlify, you may also need to log in or create a new account if you don't already have one. Once you've signed up and verify your e-mail, you will then be taken to the account area in the browser, where you can also create a new project. Don't do this in the browser since we're going to be doing this right here inside the Terminal. You may also be asked to authorize in Netlify CLI on your very first use too. I'm already logged in since I've used this many times in the past. So back over to this Terminal, we need to answer a few simple questions to get started. First of all, we don't already have a new site, so we're going to create one inside of Netlify. I already have my team names setup in Netlify, so I'll hit "Enter." We can also add in a site name if we want. This is optional. We can also just hit "Enter" and Netlify will generate one for us. Next, the public directory, and this is the location inside of our project where our newly built site is going to be stored. We're just seeing ours is stored inside of the disk folder. So I'll hit "Enter," and this will allow a deploy a draft website. Here, if we just stretch this a little bit wider, we have a website, draft URL, open this draft URL up inside the browser, and then we see a live URL which displays our website. We can click around and check this all works. This all looks fine so far. We see our card. We can also edit and we see all the various pages and categories too. However, though, on the end of our main URL, let's try going into the admin. Well, we see page not found, and also, if we go to an unrecognized URL, we see the same message. The error is because we built a single-page application, meaning when we visit this application, we'll get a single JavaScript bundle, which we already know about. We then navigate through our applications pages on the frontend using the router. When we type in /admin or in fact, forward slash anything ourselves manually, we're then making a request to the server and expecting this page to be available. However, though, as we know, we've got a single-page application, we don't have multiple pages to request from the server. Instead, when we use things like the router link, this will intercept the section just here and then find the exact part of the JavaScript we need to display inside the browser. The solution for this when using single-page applications is to ignore any additional page requests and always return back our main homepage. Then on the frontend, our router can then take care of any pages which need to be displayed. Do this and only serve up this homepage on any request. This is pretty simple to do. Head into the newly created dist folder, and inside of the root of our disk folder, create a new file, which is called _redirects. The syntax to only return our homepage looks like this. It's /* /index.html, and then 200. This tells the web server regardless of what page we're trying to request, to only ever return back home main index.html, and also the service status code of 200, which means everything is okay. Always returning this okay message means we do need to handle any errors on the frontend and we're doing this with our not found component. Give this file a save and we can redeploy your application, which we're going to clear the Terminal and run netlify deploy for a second time. The published directory is still dist. Then call the draft, and we see that all the pages is still working. If we try to go to forward slash, then any additional page requests. Netlify is then responding back with our main index page. Then the vue router recognizes we have the admin component to load up, and this is why we see the admin on the screen. Let's try the 404 page with an unrecognized route and we'll see page not found. Okay, good. Once we're happy that everything is working, we can now get rid of this draft URL and generate a live production version. The way we do this is, once again, to run Netlify deploy, but this time --prod to signal that. We're happy with everything we see and we want to finally push this to production. There we go. Our deploy is now live, and we see our website URL rather than the draft. Open this up. There's our finished version. Let's just test our unrecognized routes once more. There we go. If we also go into our Netlify account, login if you need to, we now see our sites just here. Click on this, and this is the same URL which we've just seen inside the browser. This is a live link and it will be available for anybody to see on the web. We can also access our domain settings too. If you want a custom name in the options, we can edit the name, and you can also change this to be something more descriptive, as long as the name hasn't been used in the past by somebody else. This will also have the.netlify.app extension when we're using this free plan. Or you can also use a fully custom domain if you already have one. Or you could also purchase one from Netlify too. Lastly, Netlify also makes it really easy to have a secure site by default, we can see, if we click on this, we have the padlock symbol just here. Meaning by default, our site is loaded up with a SSL certificate, meaning our site is secure. We can also see this down at the bottom of our settings, underneath the HTTPS. If you've also uploaded a custom domain, you can also enable this free of charge too. This is our site now deployed on the web, which you can now share with friends and family.
138. Thank You!: Welcome, you've made it to the end. Congratulations on getting this far. I really hope you've enjoyed the course and got lots out of it, and also enjoyed building the projects too. This is it for now. Keep on learning, keep on building things, and I will see you in the next one.