Hello. This is Jim Cooper, and welcome to this Vue.js Fundamentals module on getting started with the Vue CLI. In this module, we'll spend a few minutes gaining a high‑level understanding of Vue.js applications, and then we'll dive into creating our first project with the Vue CLI. We'll even start writing some of our own code in this module, including building this home page for the Build‑a‑Bot application that we'll build throughout this course. We're going to have some fun in this course using Vue to create a simple website for building and buying custom robots. This application will be rich enough to demonstrate all the fundamental concepts you need in order to create your own applications with Vue, but it will also be simple enough that we won't waste our time building unnecessary features that have no educational value. And this is what it will look like when we're finished. As you can see, I can come to the build page and start customizing my robot, and I can build the robot that I want and then click Add to Cart to add it to my cart. And then you can see it shows up in my cart here, and I even show which robots I saved money on, if they happen to be on sale, and back on the build page, I can click on any of these parts and learn more about the individual parts. This site doesn't seem like much, but there's a ton of concepts and learning built into this. It will be just enough to allow us to dive into things like creating components and child components, communicating between those components, routing and navigation, state management, and even API calls to a separate server. We'll even explore how to build and deploy this application to production. So let's jump right in and start learning about Vue.
At the heart of a Vue.js application, you find the Vue instance. A Vue instance, is created whenever you call new Vue. In a typical Vue application, you will only ever have one Vue instance. When you create a view instance, you will usually tell it to render a component, which you do by passing in an options object with a render function, which tells it to render an app component. This app component is the top level component in what will become a hierarchical component structure. So you can imagine your Vue instance wrapping your entire application, and inside that, you have your app component, and the app component may have a child component, which then may have other child components, etc. This should feel familiar if you've written any Angular or React applications. The component structure is similar other than the Vue instance. In a Vue application that includes routing, which is probably most view applications, there's another piece. Basically, instead of the app component containing other components, or maybe in addition to it containing other components, it will also contain a router‑view, which shows the content for the current URL, and as the URL changes, the content inside the router‑view is updated. And while there is a lot more to a Vue application, this is a pretty good conceptual overview of the main structure of a Vue app. To understand the rest of the details, let's jump into creating an app from the ground up.
We're going to need a few tools installed to get going. First, we'll need Git installed. I'm on Windows, so I'm going to use Git for Windows, which can be downloaded from here. If you're on a Mac, you can install SCM Git from here, and if you're on Linux, you can just run a command like this one, from the terminal. I'll go ahead and download and install Git for Windows, and then I'll run that, and I'll just use the defaults throughout the install. And now that that's installed, I have this new git BASH console that I can run, so we'll use that in a minute. And actually, while I prefer to use this bash console, sometimes Windows users have issues with it, so if you're getting weird errors, try using the Windows console window instead. The next thing I need is Node. I prefer to install node using Node Version Manager just because it makes it easier to switch between versions of node. as I develop different applications. On Windows, I can download that from here. For Linux and Mac, you can just run this command from the terminal. So I'm going to go ahead and install NVM for windows, and I'll just use all the default options here, too. Okay, now that that's installed, I'm going to open my Git BASH console. On windows, be sure to open it as administrator. Okay, now that I have NVM installed, I can just run nvm install and then the version of Node that you want to install It's recommended that you use this version for this course if you're following along, to avoid any compatibility issues. And now I can just tell NVM to use this version of Node. Okay, we're good to go with Git and Node now. If you have problems using NVM to install Node, you can try installing it directly from here, just choose the correct installer for your system. Okay, let's get coding.
Alright. In this clip, we're going to start creating some of our own content. We won't be exploring any new concepts just yet. We're just going to spend a little bit of time replacing this generated content with some of our own content. This will get us a little more familiar with what the CLI generated and with the layout of our new app before we start digging into key Vue.js concepts. So let's start by renaming this HelloWorld component. This will become our new home page, so let's name it HomePage.vue. We could just name it home.vue, but one of the Vue style guide rules says that components should be two words at a minimum, so we'll name it home page, and let's update the component name down here, too. And then we'll have to update the import for this in the app component. So instead of importing HelloWorld, we'll import HomePage from HomePage.vue, and then we'll use that down here and we'll change it up here in the template and we don't need this message attribute anymore. so we'll delete that. And I'd like to put all of this inside a main element, and we don't want this Vue logo up here anymore, so we'll delete that. Alright, and then let's add a couple of styles and clean up some styles that we don't need. So down here in the Style section of our app component, I'd like to add a gradient to the body of our application. So I'm going to give it a background and I'm going to set linear‑gradient to a gradient from top to bottom. And let's fix that spelling error. And then I want to set the background attachment to fixed. And then down here on the app element, I'm just going to remove everything except for a font‑family. And by the way, this app element is just referring to this div up here that has an id of app. And then I just want to add a few styles for this main div, so down here, I'll add a main style and I want to set margin to 0 auto so this will center the main content and we'll add some padding and give it a background‑color of white and a width of 1024 pixels and then just a min height of 300 pixels. Okay, we'll talk more about components and styling them in the next module. We're just trying to get a decent starting point set up here, and then we'll delve into this stuff more. So let's go see what our site looks like now. Okay, ignore the main content area, but notice that our background has a gray gradient now, and our main content area has a white background. Now let's go replace all of this content with our home page content. So back in our HomePage component up here in the template. Alright, so let's delete all of this content inside the outer div and we'll change this class to home. And then I'm just going to go ahead and paste a little bit of content here. If you're following along, go ahead and paste this in, and then notice that we just have a couple of divs here, one just has an image of a robot and the other has a Get started link. So notice that this image is loading robot‑home.png file out of the assets folder, but that file doesn't exist in here. This is just the Vue logo that came with our Vue CLI sample page. So let's go download that robot‑home.png file. To help us with this and a number of other tasks throughout this course, I've created a little helper repo over here in GitHub for the Vue.js Fundamentals course. And there is just a few files in here that we will occasionally use to download things as we go throughout this course. So in the Source folder, notice we have an Assets Folder, and here is the robot‑home.png file. And we'll actually need this other image too so we might as well download them both. So first, I'll click on robot‑home and then click on Download and then Save image as, and then I just need to go find my build‑a‑bot folder, and in the Assets folder, we'll just save it here, and then we can go back and do the same thing for this build‑a‑bot logo. So we'll save that here too. Okay, cool. So now that robot home image exists in our assets folder, now we just need to clean up some styles down here, and we don't need any of these. So I'm just going to add a few classes here. So we have a home class and I just want that to be text‑align center and then a robot class, and I just want to set the height to 300 pixels, this is for that robot image. So let's go see what our site looks like now. Okay, cool. This is looking great now. And you may have noticed that I didn't need to hit refresh on this page in order to see my changes. That's because we are using behind the scenes. Vue CLI is using the webpack dev server, including hot module replacement. So as we make changes to our code, the changes immediately show up in our browser without us having to refresh. Okay, awesome. So now we've created a new home page, but let's add one more thing that we're going to use throughout this course. Our site is missing a navigation header, so let's add that quick, and we could add that to our home component, but then when we navigate from away from our home page, we would lose that navigation header. So really, we want it to be on our app component. So up here, just above our main element, we're going to add a header element and that's going to have a nav element. And this nav element is nothing special, it's not Vue specific, it's just a normal HTML element that comes with HTML5. And then inside here, we're going to use an unordered list to list out each of the nav elements, and for now, that's just going to have one list element, and that will have a nav‑item class. And then inside here, we're going to have an image, which will be our logo so we'll give that a logo class, and this is going to be the other image that we downloaded. So in our Assets folder, it is the build‑a‑bot‑logo.png file. And then we'll just say, Build‑a‑Bot here. And now we just need to add some styles to style that. Again, we'll cover styling in more depth in the next module. But rather than have you type in all the styles, let's just go over to our helper repo, and in the src folder, there is this header.css and we're just going to click on the raw version of that and copy all of it, and then let's just paste that all in right here. Okay, so you can just see we're just adding some header and nav item styling here and some styling for our logo. Okay, let's go take a look at that. Alright, awesome. So you can see here, again, without having to refresh, hot module replacement just updated our page for us, and we now have a navigation bar at the top with a list item here on the left for navigating to our home page and that's not a link yet or anything. We'll update that later. But now we have a home page with a navigation bar in our app component and I realize that this clip was just a bit of just following along, not a lot of new concepts, but hopefully as we moved around in the app and replace some HTML in our home page component in our app component, you began to become a little bit more familiar with the layout of our application, and hopefully that will help a bit going forward. In the next module, we're going to dive into components and template syntax and start learning a lot more concepts. So in this module, we just learned some high level things about Vue and how to use the Vue CLI to create a project and a little bit about working within components and making some HTML and CSS changes. In the next module, we're going to take a much deeper look at components and templates in text, and so let's go jump into that.
In this module, we're going to explore all the fundamentals of creating Vue.js components and working with component syntax. This includes creating components, using bindings to display data and handle events, conditionally displaying elements with v‑if and v‑show, showing elements with v‑for, styling components, working with component lifecycle hooks, and reducing duplication with mixins. There's a lot here, so let's get started.
Alright, now the real fun begins. Let's start creating our own components. We already have a home page component. Now we want to create a page where we can build our custom robots. A new page means a new Vue component, and I could just put that new component right here in the Components directory, but I don't think that scales very well as applications get larger. I prefer to structure my apps by feature areas. So let's make that change now. We'll start by creating a new home folder, and then we'll move the HomePage component into that folder. And now I can delete this empty components folder. Alright, now we just need to update the reference to our HomePage component over here in our app component, it's now in the home folder, and that should be rendering fine so let's go take a look at that. Okay, yep, that's working just fine. Now let's create our new component. So this is going to be a RobotBuilder component, so let's create a new area for building, we'll call that the build folder, and then in here, we'll create a new single file component called RobotBuilder. Remember, component names should be two words. Okay, so let's create our three sections in here, so we'll have a template section, and a script section, and a style section. Alright, our template is complaining just because we don't have a root element in here yet. We'll fix that in just a minute. First, we'll go ahead and create our component element and export it. So we're just going to export an object and we'll give it a name. So this is our RobotBuilder. And notice that ESLint ran when I saved it and it just remove some extraneous spaces at the beginning so that ESLint will run every time I save. Okay. Now for the HTML and CSS. I don't want to bore you with typing in a bunch of HTML that isn't really Vue specific. So let's copy this from our GitHub repo. So back over here in this source folder in the build folder, you can see I've created an HTML and CSS file for this component. So let's click on the HTML file, and then we'll click on Raw and copy this, and then we'll paste that back into our template here. Okay, so you can see that we just have a bunch of HTML in here. There is nothing specific about Vue here, but you can see that we have a top section, a middle row section, and a bottom row section, and you can see the part selectors here, they basically are comprised of an image and buttons for left and right or up and down for changing the selected part. Just as a reminder of what we're actually building here, it's this Build page here, so this is our top section, our middle section, and our bottom section. And then here are the part selectors, what I'm calling part selectors, which consist of an image and buttons to select the individual parts. And so, that's what all this HTML is here and these buttons are not wired up to do anything yet, but you can see I have a lot of classes here, so let's go grab the styles for this file also. So back over here in our repository, we'll go back and back again, and I'm going to click on this RobotBuilder.css file and grab the content out of here and then we're going to go ahead and paste that into the style section down here. So this is just all the styles for positioning each of those part selectors and the buttons and everything. And again, there is nothing really Vue specific here with regards to what I pasted in. This is all just CSS styling. The only thing that's Vue specific here is that I put the CSS inside this style tag in our single file component. Okay, and then you can see these image tags are pointing to images that we haven't downloaded yet so let's go grab those. And back over in our GitHub repo, you can see inside the build folder there is an images folder and there is a lot of images here. I could just click on each one of these and download them one at a time, but it will be easier to just clone this repo and then copy them locally. So I'm going to go back over to my terminal and stop my server and then I'm going to move back a directory. And then I'm going to git clone this repo here. Okay, let's go ahead and clone that. Okay, now that that's there, I can go back into my build‑a‑bot directory and then we'll change into the src/build directory and we'll make an images directory here, and we'll move into that directory. Okay. Now we can just copy those images from the cloned helper repo like this. So we'll just grab everything out of the images directory and copy it into this directory. Okay, so now you can see we have all those images in here. Alright, now that we have those, let's move back to our application directory and we'll go ahead and start the server. Alright, now let's go take a look at what our new page looks like. Of course, how are we going to get to this page? We don't have routing set up yet, and so how are we going to view this robot builder, instead of the home page? Well, for now, we'll just have to import it over here also. And then we'll just go ahead and render this here instead of the HomePage component and we'll comment this out now. And then we just need to use this up in our template here. Okay, so now this should be showing our RobotBuilder, instead of the home page so let's go check that out. Okay, perfect. So you can see that we have our new robot builder component rendering here, but these buttons don't do anything yet, but this is awesome. We have a new component and it's just got some really basic HTML and CSS, and all we're doing so far with the component is just exporting it and then using it here in our main app component. So let's make this page more interesting next by adding some binding so that we can start selecting the individual parts.
In order to make our new build page a little more dynamic, we need to start by binding it to data from the component instead of these images being hard coded in the HTML template. In order to provide data to our component, we need to update our component options to have a data function. So down here, we'll add that right here. So this is just going to be a function, and right now we'll just return an empty object. If we look at our build page, we have all these different parts: heads, arms, torsos and bases; and eventually, when we click these buttons, the corresponding part will be swapped out for another one. We're not going to wire up these buttons just yet, but let's get the images wired up to a list of parts. First, we need some data. Eventually, we'll get the data for the parts from an API, but for now, we'll just create a file with all the data in it. So, let's create a data folder inside our source folder, and inside that folder we'll create a new parts.js file, and this folder is also going to contain our images. So let's move that images folder from our build folder into the data folder. Okay now we need our parts data. So let's go back to our GitHub repo. Let's go into the data folder and grab this parts.js content, and we'll paste that into here. So, this is mostly just JSON data. But you can see up here, this first line there's some webpack goodness here. So we're using webpack's require.context here to make webpack aware of the image URL so that we can just use them throughout this JSON data as relative URLs. So down here on line 10, you can see that we're calling this images function and passing in a relative URL. When this is compiled by webpack, this will point to the relative URL of this image file. So other than that, this is all just JSON, and you can see that we have this parts variable up here, our constant, and that we're exporting it down here, and just so we understand the shape of our data a little bit, if I collapse each of these sections, you can see that the parts object is an object with properties for heads, arms, torsos and bases. Each one of those is an array of parts. So this state is perfect for what we need. So let's jump back over to our RobotBuilder, and then let's import that data here. So we will import availableParts from data parts. So the ESLint plugin for Visual Studio code is a little bit funny. You might notice that I hit save multiple times there, and it reformatted according to my linting rules a couple of times. Okay, so now we just want to return these available parts as part of our data object. So we can just return it like this. So now our data object is an object with available parts property that is set to that JSON Data. Now let's see how we can use this state in our template. At the top of our template here, we have this image that is displaying currently the head image for a robot, and instead of setting the source to a hard coded string like this, we want to bind it to an expression. We can do that like this. So this says bind the source attribute of this image element to this expression in the quotes, and inside that expression, we can access available parts. So this is the availableParts object that we exposed in our data function, and then available parts has a heads property, which is an array and we'll grab the first head and then grab the source property. So this source property thinks to webpack and we talked about this just a minute ago. This is the relative URL to the image for this head. Okay, so let's copy this expression down to each of the other parts. So we'll set these to be bind also, and for now, we'll just bind them all to the heads. Okay, and there's just one more here. Okay, so if we look at this, we now have a robot with all heads. Not a very useful robot. So let's just update each one of these to point to the correct part type. So here, instead of heads, this is going to be arms, and this is going to be torsos, and this one is also arms, and then this is bases. Now if we refresh, there we go, now we have a functional robot again, and that's all thanks to the v‑bind syntax, which is binding the source attribute of the image element to these expressions. Of course, you can bind any attribute of any element using a v‑bind. So now our image source attributes are all bound, but we can't change them. We still are not changing the image when we click these selector buttons. To do that, we're going to have to bind to the click event. Let's see how to do that next.
In order to react to actions by the user such as clicking on these buttons, we need to bind to the events of those elements. That's just as easy as binding to data was. All we need to do is use the v‑on syntax. So, on this button, I'm going to add a v‑on binding to the click event. So now whenever this button is clicked, the expression inside these quotes will be called. So, what I want to do here is I want to call a function. So I'm going to call selectNextHead. So this is the next button for the head image. So, when the user clicks on this button, we're going to call the selectNextHead function, and that function needs to exist on our component. To do that, we're going to introduce a new property on our component called methods. And that's an object, and then you define each of the functions inside of here that you're going to call from your template. So we need selectNextHead. And for now, let's just console.log('selectNextHead called'). So let's go see if that works. So, over here, let's open our console, and then click this button. There you go. You can see each time I click it, this selectNextHead call gets logged. So our v‑on click binding is working. But what we really want to do is have it select the next head instead of just console logging. So we're going to have to track some index and increment and decrement it when the buttons are clicked. To do that, let's add a selectedHeadIndex to our data object, and we'll initialize that initially to 0. And then in our selectNextHead function, we will increment that like this. And then to make that work up here in our template, instead of hard coding this to 0, we're going to use our new selectedHeadIndex. So now this will grab the head that matches that index and bind the source property to the URL of that head. Okay, let's go check this out. Cool. So now, as I click on this button, you can see that Vue is calling our selectNextHead function, which is incrementing the index, which updates the binding for that image. Okay, let's do the same thing for selectPreviousHead. So we need, on this previous selector, we need a v‑on click binding, and that will call selectPreviousHead. So now let's go create that function down here, and that will decrement that index. Let's go check that out. Close our console here. Alright, cool. Now, I can move back and forth between the heads. Of course, we have a slight problem here. If I open the console, clicking on this next button, you can see that at some point I start getting an error. This is because we are incrementing that index out of range. So basically, we need to make it so that when we get to the end of the heads, then when you click the next button, it resets the index back to 0. So to fix that, I'm going to create a couple of helper functions up here at the top of my script block. Okay, so we have a getPreviousValidIndex function and a getNextValidIndex function. And if you're following along, go ahead and type this in, but you can see it's just going to, when you're clicking getPrevious, it will deprecate it until it gets to 0, and then it will set it to the last head and then vice versa with the getNextValidIndex function. And then notice that these functions take in the index to increment and the length of the current parts array. So, down here, instead of incrementing this, we're going to call getNextValidIndex, and we will pass in this.selectedHeadIndex and the length of the heads array. Okay, and then we'll do the same sort of thing for getPreviousHeadIndex. Okay, so that should fix this problem. And then these functions don't mutate the index, they just return a new index, so I need to set the selectedHeadIndex to whatever's returned from these. And our linter is complaining because these lines are too long, so let's just wrap them, format them a little bit better. Okay, let's go check this out. So now, as I increment, you can see that it is wrapping around, and we're not getting any errors and same for going to the previous heads. Awesome, so now we know how to bind to the click events and, of course, we could bind to any event that is fired by any element. So we've updated the head, but we have not yet updated the other parts, so let's go fix those bindings. So if we go back up to our template, I'm basically going to need to copy these bindings down to all the other buttons. So there's the previous binding, and then the next binding. And then this will have to call, this is the left arm, so I'm going to call selectPreviousLeftArm and selectNextLeftArm. Okay, I'm going to go ahead and do the same thing for all of the rest of the parts, but I'm going to go ahead and do that off screen because it's not too interesting to follow along. Okay, so you can see here that I've updated the bindings for selectPreviousTorso and NextTorso, RightArm and Base. And then notice also that in the v‑bind:src bindings, I've also updated the hard coded 0 to the selectedBaseIndex, RightArmIndex, and TorsoIndex and same thing for LeftArmIndex. And then you can see down here that I've initialized them all to 0 here in our data function, and then I've added methods for each one of them, selectNextHead and PreviousHead, LeftArm, Torso, RightArm, and Base. So if you're following along, go ahead and copy and paste all those and update them appropriately. Okay, so let's go check this out. So, now we can move to the next and previous head and left arm, torso, right arm, and base. Awesome, we have a functioning robot builder. In the next clip, we'll take a look at an abbreviated syntax for these v‑bind and v‑on bindings.
We just created all of these bindings in our RobotBuilder HTML, and I just wanted to take a second to call out a more abbreviated syntax that's really nice. Anywhere we use this v‑bind syntax, we can replace it with just a colon, so this is a shorthand syntax for v‑bind. And then anywhere we use v‑on, we can replace that with an @ sign, so no colon here. I really like this syntax because it's very non‑obtrusive, but it's also really clear that we're creating bindings. So let's replace all of these with a Search and Replace. So I'm going to search for v‑bind and replace it with just a colon. So I'm replacing v‑bind: with just a colon, and I'll just replace all of those. And then I'm going to search for v‑on: and replace it with just an @ sign, let's replace all of those. And our RobotBuilder should still be working, let's check it out. There we go, we can still see our images, our bindings are working, and our button bindings are all working. So from here on out in the course, we'll be using this abbreviated binding syntax. Next, let's take a look at how we can create and bind to computed properties.
Sometimes you want to be able to easily bind to something in your HTML template that involves a complex calculation. Doing complex calculations in your HTML template isn't really a great idea. We can solve that and move the complexity into the component with computed properties. All of these expressions are a little ugly. What would be nice is if we had a selected robot property on our component that we can use. So, if we come down to our component, we can add a computed property here, and like the methods property, this contains methods. So, I'm going to add a selectedRobot function, and this will return an object that represents our selectedRobot. And then that object will have a property for each part, like this. Now, let's add the other parts, so there's the leftArm, and then we'll do the torso, and then the rightArm, and then finally the bases. So now selectedRobot.head will return the currently selected head, and so forth. So now up here, instead of these expressions, we can just replace these with selectedRobot.head.src. So let's copy this down to each one of these. So there's a leftArm, and then here is the torso, and now the rightArm, and finally the base. And then I think I might have had a typo down here, yeah, I didn't say LeftArmIndex here. Okay, so that should be working great. So if we come over to our RobotBuilder, now we can still use our RobotBuilder, but our code is a lot more simple in our template where we don't have this complex expression here, but we're just binding to the head of the selectedRobot. So this is better, and while this one just helps simplify our template a little bit, there are cases where it can simplify them a lot. You really want to be careful not to do too much logic in your templates, and computed properties make that easy.
Our robot builder is looking pretty good, and we've learned how to bind to attributes and events, but we haven't yet learned how to simply just display raw data on the page. We do that with interpolation. To demonstrate that, let's display the name of our robot just above the robot head. If we take a look at the parts data over here, you can see that each part has a title, and the names of the heads actually make for a pretty good name for our robots. So let's display the name of the currently selected head above our robot. Because of our computed property, this is going to be really easy. So let's add a new div right here, and we'll give it a class of robot‑name. And let's add that class down here in our styles. And I'm going to position this absolute, and I want to position it about 25 pixels above the top of the head, and then we'll align the text to the center, and then we'll set the width to 100%. Okay, now for the interpolation part. Back up here inside our div, we're just going to add an expression like this. So, the double brackets is used for interpolation, and inside there we'll just put an expression. So we just want to use selectedRobot.head.title. And this is it. That's all there is to interpolation. And you can put any valid expression in here. Although, again, it's recommended that you only use simple expressions in your template. Okay, let's go check this out. All right, awesome. You can see here that our robot has a name, and then you can see as I click through the robot heads that the robot name up here is changing due to that interpolation expression. So, interpolation is really simple. Before we wrap up on interpolation, though, let me mention one more performance option. We can add a v‑once tag to any element like this. And when you do this, any bindings inside that element will be evaluated once and then never again. This is handy for performance reasons if you have a page with a lot of bindings that are going to render once and then you don't expect the data to change. In our case, though, this isn't what we want. If I come over now to our robot builder and cycle through the robot heads, you can see that the binding isn't updated. It updated once when the page was rendered, and then it's not being updated anymore. So we don't want that here, so let's remove it. But it's good to know about for performance reasons. Now that should be updating again. Okay, cool. So next we're going to take a look at ways to dynamically show and hide our content on our page.
Vue supplies two directives that we can use to conditionally show or hide content, v‑if and v‑show. Let's check them both out and talk about when to use each one. To demonstrate this, let's add an indicator next to this title right here that indicates whether the robot is on sale. If we look at our parts data again, you can see that some of the parts have an onSale attribute. So we'll bind to the onSale property for the head, and if the head shows that it's on sale, we'll indicate that the robot is on sale. So back in our robot builder, right here next to the interpolation expression that's showing the title, we'll add a span with a class of sale, and then we'll just add the text Sale! inside here. Okay, lets go add that class to our CSS down here. Okay, so if we go look at this right now, it appears that all of the heads are on sale, but that is not accurate. There is actually only one head that's on sale, and it's our Friendly Bot. So let's use v‑if to only show the sale span if the head is on sale. So right here, I'm going to add a v‑if binding. And then the expression inside of the v‑if just needs to be a Boolean. If the expression evaluates to true, the element is shown. So we'll just put selectedRobot.head.onSale. Okay, so now if this onSale element exists on the head and it's set to true, then this span element will be displayed, otherwise it will not be displayed. So let's check that out. Okay, cool. That's working better. Now only our Friendly Bot appears to be on sale, which is correct. Okay, so that's v‑if. Now let's check out v‑show. It actually works identically, so I could change v‑if to v‑show. And then if we go look at this, it's still working just the same. So you might wonder, well, what's the point? Well, the difference is what's happening behind the scenes. If we take a look at this element, we'll inspect it here, and then let's navigate to one that is not on sale. Okay, you can see that the element is actually still here, but it has a inline style now that says display: none. And so that is how Vue is showing and hiding this element when we're using v‑show. It does so by adding style="display: none;". However, If we change this to v‑if again, and then look at this again, you can see that actually, the element isn't even here. It's been removed completely from the DOM. And as I move through this, if I hit the one that is on sale, then the element shows up again. And so, v‑if actually adds and removes elements from the DOM, whereas v‑show leaves the elements there and just styles them differently. So the question is, when should we use v‑if and when should we use v‑show? Basically, if the content that you are showing and hiding is expensive to generate and it's going to be shown and hidden frequently, then you should use v‑show because Vue won't have to go through the expensive rendering process each time it's redisplayed. But if your content is not expensive to render, like our current example, or if it's not going to be hidden and redisplayed frequently, then v‑if is perfect. Right now, we're just showing and hiding a span element. But imagine if this was a view component that we were showing and hiding and imagine that that component had a child component, which also had child components. You can see how that can get really expensive to rerender. In that case, v‑show would be better. In our case. v‑if is perfect because it's just a span. It's not expensive to render at all, so we'll leave it with a v‑if. Next, let's take a look at how to repeat elements using v‑for.
By now, you've probably gotten used to the idea that we can style our components down here in the style section. Although we haven't talked about this directly, as we've proceeded this far in the course, we've been adding to these styles and you've seen how it immediately affects the component, but we haven't really explored what's actually happening here or how far reaching these styles are. To explore this a little more, let's jump over to our app component. Notice in the styles of our app component that we're styling things like this main tag and the header tag, and you can see those main and header tags up here in our template. But what about this body tag? That main tag does not show up at all up here in our template, and yet, if we look at our app, the gradient that we're adding to the body tag is showing up here in the background of our app. So how is it that the body style that we're adding here is getting applied to a body tag that's not part of this component? The body tag is actually part of this index.html file that was generated by the CLI. So notice we have a body tag here. So this index.html file is what is actually loaded when our app first loads, and it has a body tag. And then you can see down here on line 14 that we have this app element, and this is what the render function is binding to, and so this is where the rest of our app is displayed. But it's concerning that we have a style in our App.vue component that is styling something outside of that component. But not to worry. There's an easy solution to this. Back here in our app component, on the style tag right here, we can add a scoped attribute, and if we go back now to our page, you can see we've lost our gradient. So we just essentially scoped all of the styles inside this style element to only this app component. That's pretty awesome, but how does Vue accomplish it? Let's take a look at the source of our page in the browser. So I'm going to inspect this, and then notice on this header element that it has a data attribute with a hash after it, and if I go back over here and remove this scoped attribute, and then refresh, notice that the randomized hash has disappeared. So first of all, the scoped attribute is adding those data attributes. So if we go back over here and put that scoped attribute back, and then take note of the header styles that are being applied here, the background color, the width and the margin. Now, if we go look at the source again and refresh, take a look over here in the inspector that is showing the styles for header. Notice that it's not just being applied to any header element. It's actually only being applied to header elements with this data attribute. So that's how Vue handles scoping styles. It puts these randomized hashes on scoped elements, and then targets them specifically with that hash. So a header element on another component would either not have this data attributes at all, or it would have a different data attribute, because it's in a different scope. And the same concept applies to child components, as we'll see in the next clip. With some exceptions, if you add the scoped element to a parent component, its styles won't bleed into the child component. We'll take a closer look at this in a minute. But first, let's talk a bit about scoped versus global styles. As a general rule, you should not have global styles in any component other than your top‑level app component. So let's go fix our robot builder so that it has scoped styles. So over here in the style element, we'll add the scoped attribute. And so now we don't have global styles in our robot builder, but the app component is a good place to have some global styles. So let's do that. And the interesting thing is that in our app component, we want some global styles, but we also want some styles that apply only to the app component. But that's okay, you can do that. You can have multiple style blocks and one can be scoped and the other not. So now we have a global style block here and a scoped style block down here. And let's move this body style up here to our global style. And the rest of these styles are specific to the app component, and so we want to leave those in the scoped style block. So now if we go back and refresh, and let's close our console, you can see that we have our gradient background again. And so the body style is being applied globally, whereas the rest of these are only being applied to this component. Okay, cool! So now we'll take a look at how this all affects child components.
It's important to understand what it means when you add the scoped attribute to a parent component and how that affects child components. Right now, our RobotBuilder is a child component of our App component. And if we take a look at our RobotBuilder, you can see right here we have this robot‑name class. Let's add an element in our App component with that same class. So right here just above our RobotBuilder, we're going to create a div element with a class of robot‑name, which happens to match the robot‑name class inside our RobotBuilder. And then we'll close that div here, and we'll just say This is a test. And then down here in the scoped styles, we will add that robot‑name class with color red. Okay, so if we go take a look at this, you can see that the robot‑name styles got applied to the element in the App component, but not to the element with this matching class name in the child component. So this is what we would expect. It is scoped to our parent component. But I want to point out something. Watch what happens if I come up here and wrap this div around the RobotBuilder. So now this div is wrapping the RobotBuilder. So let's see if that changed anything. Now you can see that both the parent component and the child component got styled. So it appears that the parent component styles are bleeding into the child component, and this is sort of true, but not really. At least it's not true as far as Vue is concerned. Let's see what happens if we also add a border to this style. So down here I'm going to also add border, and we'll add a 2‑px solid blue border. So now if we come back over here, notice that we do have a border that is wrapping the div in the App component, and that div stretches across this because the RobotBuilder is wrapped by this div. But the interesting thing is that even though the border is being applied to the robot‑name class, it is not bleeding down into the child component's robot‑name class. Otherwise, we would see a border around this robot title here. So it seems that the font color is bleeding into the child component, but the border is not. So when Vue says that its styles are scoped, what it means is that it's not going to apply that style directly to classes or elements outside of the scope. But that doesn't mean that the child elements won't be affected due to CSS inheritance. That's just the nature of CSS. In CSS, there are a number of CSS properties that get inherited. Font color, for example, is inherited, whereas border is not. That's why you see a difference here. So know that when you style something in a parent component that wraps a child component, traditional CSS inheritance will still be at play. But Vue won't specifically target elements outside of the component if the style is scoped. So it's important to understand that difference between Vue targeting an element or applying classes to an element in a child versus styles just getting inherited because they are applied to a parent element by CSS. But what if you do want to be able to style a child component from the parent? Vue does allow a parent to style a child component within the bounds of scoped CSS. So let's take a look at how to do that. The easiest way to think about this is to consider the root element of a child component part of the parent scope. So over here in our RobotBuilder, this element is the root component or the root element of our RobotBuilder. And notice that it has a content class. So if we go to its parent component, the App component, and let's remove this test div. We don't need this anymore. And now down here instead of robot‑name here, let's change this to just content. So now we're applying these styles to the content class, and there is no content class in our App component. It's only in our RobotBuilder. So let's go back over here. And there now you can see that the red text and blue border are being applied to the content class in our child component. This is only because that content class is on the root component of the child, and so you can use the root component of a child component to allow a parent to apply styles to the child component. But what if we want to access this robot name from our parent and style it? So over in our App.vue, we could add .content .robot‑name. So, generally, you can target things in CSS like this where you can target a robot‑name class that's contained within the content class. And since we're able to target the content class from the parent, the question is, Are we able to target elements inside of that content class? So we would expect to now see a blue border and red text around the robot‑name. So if we come take a look at this, you can see that our style's not getting applied anywhere. If you want to be able to do deep targeting like this, you need to use the deep selector that looks like this. Now if we take a look at it, there now you can see that we can target it. So you can target child elements within scoped styles like this by starting with the class of the root element and then using the deep selector to select other items within the child component. But you need to be aware that this deep component is truly deep, like it is not just the child component, but this will also affect child components of your child components. So it will keep going down the chain. And it's possible that this won't work with some CSS preprocessors. And so in that case, there is also the deep selector, and that has the same effect. Okay, we don't want to keep this here, so let's delete that. That covers everything for styling child components. Next, let's take a look at conditionally applying styles to elements.
Let's see how to replace this style binding with a class binding so that we can use classes instead of inline styles. First, let's start by adding a class down here. We could call it sale‑border, and that will just have a border that is 3 pixels solid red. And then back up here where our style binding is, instead of this, we'll use a class binding. And then a class binding is basically an object where the keys are the names of the classes that you'd like to toggle. So in this case, we have a sale‑border class. And then you just set that to a Boolean expression that will evaluate to true when you want this class to be applied. So in this case, we want it to be applied when the selected robot head is on sale. So this is just saying apply the sale‑border class when the onSale property is true. And if we wanted to toggle multiple classes, we would just add more properties to this object. So this should be working just like the style binding was. So the friendly bot is on sale and the rest are not on sale. Perfect. Now, one thing that you might not have noticed if we inspect this, this div where we applied the sale‑border also has the classes top and part. If we go over and take a look at our template, you can see that this element has both a class binding and a class attribute on it. So the top and part classes are going to always be applied, and the sale‑border property is going to be applied conditionally so you can combine them like this. Of course, this class object could get fairly complex if you were toggling lots of classes, so you could always move this expression to a computer property like we did with the style binding. But like style bindings, there's another syntax that we could use here in an array syntax. So I could just provide an array here, and that array contains the classes that I would like to apply. So this is not conditional. This will always apply a class, so we should be seeing that everywhere. But we could then take this, and let's undo a little bit and grab this expression, and with this array syntax, let's use a computed property like we did with our style binding. So I'm going to create a computed property called saleBorderClass, and we will add the value of that to our array. So now, down in our computed properties, I can change this to saleBorderClass, and then this is just going to return the saleBorder string if the robot head is on sale, so we'll just use return array like this. So if it's on sale, then return sale‑border; otherwise, it'll return an empty string. And we need a this here. Okay, so that should be conditional again. There we go. And now that we have that using the array syntax, we could actually collapse these class expressions into a single expression. So I'm going to take these out of here and make them strings in here. And then I can get rid of this class attribute. And now it's all taken care of in our class binding. Cool, I like that final syntax, so let's go with that. And next, we'll take a look at using CSS preprocessors, like SaaS.
Vue.js fully supports using CSS preprocessors if, for example, you want to use something like Sass, instead of plain old CSS. In fact, the 0 config webpack configuration that comes with the basic CLI includes all of the webpack config to support Sass, LESS, and Stylus. All you have to do is npm install the appropriate loaders if you want to use them. So here in our terminal, I'm going to just npm install node‑sass and sass‑loader and save that. Let's go ahead and install that. And since the built‑in webpack config is already set up to handle the sass loader, we can just jump in using that in our project. So all we have to do to support sass is come down to the style section, and up here at the top where we define our style tag, all we need to do is specify the language used here, so I can say lang = scss. And now I can start using Sass syntax in here. So I could, for example, wrap this inside of the part class, and then if we come back over here and start our server again, and then come back over to our browser and refresh, you can see that all our styles so these images are still the correct size, even though we added the Sass syntax for sizing them. So that was surprisingly easy and you can see how the 0 config environment made that really simple. Of course, if you wanted to use a CSS preprocessor other than Sass, LESS, or Stylus, you could do that, but you would have to go in and modify the webpack config, and we'll talk about that in the deploying to production module. But out of the box, those three are already supported, and now our robot builder is using Sass. This wraps up our discussion of styling in our components. We're going to next take a look at the different component lifecycle hooks that we can tap into.
There are several lifecycle hooks that we can tap into to take an action based on the various lifecycle states of a component. Let's take a look at the created lifecycle hook. A typical use case for lifecycle hooks is to fetch data from an API when a component is first created. But we're not ready to do that just yet. We'll implement that exact use case later in the module about state management when we start using an API. For now, let's just make it so that whenever this RobotBuilder component is created, we log something to the console. So all we need to do is add a created function like this, and then we can just console log in here, and tapping into a lifecycle hook is that easy. Let's go check this out over in our browser. So let's open our console and clear it, and then I'm going to refresh this, and when this page loaded, the RobotBuilder component was created, and then you can see down here we got this component created message, and again, this isn't a great use case obviously, but it's pretty common that you'll do things in here like fetch data from an API that's needed for this component. There are a handful of lifecycle hooks, but covering all of them is beyond the scope of this course. If you want to explore more about lifecycle hooks, you can check it out here In the Vue documentation. They provide this lifecycle diagram that shows all of the different lifecycle hooks: beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, and beforeDestroy, and finally, destroyed; and it shows you in this diagram here where in the lifecycle of a component each of these hooks get called, and again, we'll touch on this more later in the course. If you want to look at it right now to explore lifecycle states further, you can check it out in the Passing Data to Parent Components clip, which is inside the Enabling Intercomponent Communication module. Or you can also look at it in the State Management module when we start talking about making API calls. For now, let's move on and take a look at mixins.
Mixins are a way to share functionality across multiple components. Basically, any component option can be extracted out into a mixin file, and then when you import the mixin, it will be merged into the rest of your component options. Let's see how to do that. Let's take this created lifecycle hook and move it into a mixin. So we'll grab it from here, and then let's create a new file called created‑hook‑mixin.js, and then here we'll just export an object. So basically, we're just exporting a component configuration object here, and whatever is in this object will be merged in with any components that you use it in. So to use it, let's go back into our robot builder, and we're going to import our CreatedHookMixin from './created‑hook‑mixin'. And then we're just going to use this down here in a mixin property, so mixins, and then this is just an array of mixins, and we're only going to have one, the createdHookMixin. Cool, now this mixin can be used in multiple components. Right now, we're just going to be using it in this one. And if we come back over to our web app and open our console and refresh, you can see our component created message is still getting logged, and so our created hook is still working, but it is now extracted out into a mixin that could be reused. Again, our application isn't quite mature enough yet for a great example for mixins. If you want to see a better example of this, in the Managing State with Vuex module, we talk about it when we have a few components that all need the same data. So we'll create a mixin in order to share the logic that will be used to fetch that data across multiple components. You can find that in the clip on using actions to work with APIs and asynchronous data. I just wanted to talk about it a little bit here in the components sections so that you knew about it. And just be aware, you can use a mixin to share any component option across multiple components, including lifecycle hooks, props, computed properties, etc.
In this module, we learned about the following component concepts: creating components, using bindings to display data and handle events, conditionally displaying elements with v‑if and v‑show, repeating elements with v‑for, the various ways to style components, working with component lifecycle hooks, and using mixins. In the next module, we'll explore various ways to communicate between components.
In this module on inter‑component communication, we'll explore how to communicate between components. You don't get far in building an application before you run into situations where one component needs to know something about what's going on with another component, so it'll be good to learn how to accomplish this with Vue. There are a few topics to cover, including using props to share data with child components, validating component props, passing data to parent components with events, and injecting content into a child component with slots. Understanding how to do this will allow us to build rich, client‑side applications with Vue, so let's dive in.
So far, we haven't really done much with creating child components other than the fact that our app component uses our robot builder component. If we take a look at this RobotBuilder template, you can see a lot of repeating code here for each of the individual part selectors. Let's turn this into a part selector component and reuse it in each of these places. When we do that, we'll need to pass down to the part selector component the parts that we want it to select from, and that will be a great way to show how to communicate between components. So we'll start by creating a PartSelector component, and rather than completing this whole thing from scratch, let's start with a partially completed component. So over here in our helper repo, in the src, build folder, we have this part selector Vue file. So let's grab that and paste that into our new component. So if we take a look at what we've pasted in here, you can see that at the top here, we have some HTML that looks a lot like the HTML that was being repeated multiple times over in our robot builder. And then down here in the component script, you can see that we have some functions for getting the next and previous index and down here methods on our component for selecting the next part and the previous part that call those functions. This should look a little bit familiar because this same code or code a lot like it existed over in our robot builder. So we've just moved that over into our part selector. And then notice up here we're importing all of the available parts and we're setting here on Line 13 this parts variable to the heads from the available parts. This is just temporary so we can get up and going. The parts for the part selector will actually be passed into us by its parent component so we'll delete this pretty quick here. So just to bring you up to speed with what the plan is, if we look at our a robot builder, each one of these parts is going to be a different instance of the part selector and the robot builder will be responsible for giving each instance of the part selector the correct list of parts for it to work with. So in the robot builder, When we create this instance of the part selector, it will pass in a list of heads, and when it creates this one, it'll pass in a list of arms, etc. This will be a great way to learn how to communicate with child components. And now that the part selector will be taking care of all of the selecting of the parts, then over in our robot builder, we can delete a bunch of stuff here, so we can delete these functions here that we saw exists now over in the part selector. And then we can delete all of these functions down here and notice how many of these there are that we're going to be deleting. And all of these will be replaced with just two functions in the part selector. So you can see how this is going to be eliminating duplication and it's going to be a lot cleaner. Alright, and then we're not going to need these selected indexes here either. And then we're just going to initialize each one of these parts in our selected robot to you just an empty object. So each one of these will be just initialized like this. And actually, now that this doesn't have any complex calculations anymore, it doesn't really need to be a computed property, so let's just move this up to our data object. And this will just be a property instead of a function and it'll just return this object. And now we're just going to go ahead and import our part selector. Alright, and then this is something that you may not have noticed in the app component, but whenever you reference a child component, not only do you need to import it, you also need to list it in a components array on the parent component like this. This is what makes our component aware of this selector so that we can now use it up here. Okay, so now we're going to replace each one of these with just a part selector. So we have one at the bottom for our base, three in the middle for the left arm, torso, and right arm, and then up here in the head, we have a little bit more going on so we can go ahead and replace all of this here with the part selector, and we can get rid of this div here. This stuff, however, we're going to want to reuse later, and so I'm just going to comment that out for now. Okay, so now we can use these part selector elements because we have declared them down here. Oh, and actually, this is not supposed to be an array, this is an object. So this is really an object that where we're saying that we have a part selector component that is set to that part selector, but we can just use the shorthand syntax like this. But you can see already how much cleaner our template is because we cleaned up so much duplication, so let's go check this out. Okay, well, we've got all heads again, so not exactly what we want, but it is what we expected since our part selector is currently just hardcoded to return heads. To fix that, each instance of our part selector needs to receive its list of parts from its parent component, so we'll do that next, but you'll also notice that our arrows on our buttons are missing and the buttons aren't exactly in the right place. So depending on where the part selector is, sometimes the buttons need to be on the top and bottom of the image, and sometimes they may be on the left and right. So the part selector will also need to know what position it's in before it knows what type of buttons to display. So we'll fix that, too. We'll get into all of that in the next clip when we start talking about how to pass data down to child components.
We need to update our robot builder to pass in the parts to the PartSelector for it to cycle through. So we'll start with this first one, which should allow the user to select which robot head they want. So that means over in our robot builder, right here we want to tell the PartSelector which parts to use, and we'd like to do that like this. So availableParts is already available to us in our robot builder's data object, and then we're just grabbing the heads off of it. And then notice that we're binding it to a parts attributes of the PartSelector. This won't work yet, because the PartSelector doesn't have a parts attribute to bind to. We'll add that next. We can do that over in the PartSelector by just adding props to the component like this. And then the most simple syntax for this is just to pass in a string array. And that's all there is to making props available to be bound to by parent components. And now that we have that, we can stop hard‑coding the parts up here, which means we don't need this import here. And then down here inside the component wherever we're using parts, this needs to be this.parts now, since we're getting that off the props. Okay, so now that we have that prop, let's go ahead and pass in the correct parts in each of these instances. And I'll just copy and paste this, and this is torsos, and this is arms again, and then this one is bases. Cool! Let's go see how this looks. Awesome! Now we're getting the correct parts, even though the arms aren't quite positioned right, and even though our buttons are still missing their arrow icons and aren't quite positioned right, if I click them, they do work. And you can see our PartSelector even supports indicating if a part is on sale. Now, in order to fix the arm positions and display the buttons correctly, we need to apply a CSS class to each one of these part selectors, based on the position of each part selector. So, for example, this part selector for the head is in the top position. This left arm selector is in the left position, this one's in the center position, this one's in the right position, and this one's in the bottom position. And if we take a look at the PartSelector, down in the styles, you can see that I've already created CSS classes to handle displaying those correctly. But these position classes aren't being applied yet anywhere in our PartSelector component, and that's partly because the PartSelector component doesn't know what position it's in. So let's come back over to the robot builder, and in addition to specifying the parts for each one, let's specify the position. So I'm going to set position equal to top, and notice there's something different about this binding. Notice the parts binding is using a colon, but the position is not. That's because position isn't really actually a binding. It's really just an attribute on the PartSelector, and we're setting it to the hard‑coded string top, whereas parts is an attribute, but we're binding to that attribute, and passing along available parts.heads, which is an expression that needs to be evaluated. So let's go ahead and add this position to each of these others, too. So we've got left and center, and then this is going to be right, and then finally down here for our base, this is going to be bottom. Okay, now we're passing to our PartSelector what position it's in. So over here, we need to add a position prop so it can receive that value. And now we're just going to use that in a class binding. So up here in the template, right here on this div, in addition to the part class, I also want to bind to the position. So remember, we can specify both a class and a class binding, and they'll get combined. And so, in the case of the head, this div will have two classes, a part class and a top class. So whatever value is passed into the PartSelector for the position prop will become a class here. And then based on those classes and the styles that we specified below, everything will get styled correctly. So let's go take a look at our component again. There we go. Now our buttons are all positioned, and you can see that some are on top and some are on bottom versus left and right, and the arrows are all there, and that all came from our CSS styling. So just to review the important parts of this, we can pass data into child components using attributes like this, and for each attribute, you just need to specify props in the child component. It's really that simple to pass data to a child proponent. We can, however, take this a little bit further and actually validate the props that are passed in to make sure that they are the right type, etc. Let's take a look at that next.
Our PartSelector component now takes in two props, parts and position, but there are no restrictions on the type of data or values that could be passed into these props. We'd like to provide some validation to prevent parent components from passing in bad data. That's actually quite easy to do with a little different syntax. Let's start by requiring the parts prop to be an array and the position prop to be a string. So instead of using an array here, we'll use a keyed object where the keys are the names of the props, and the values are objects that specify validation information. So for the parts prop, we expect its type to be an Array. And for position, we expect its type to be a String. And now that we have these in place, if we come back over to our RobotBuilder and on one of these, instead of passing in an Array for the parts, we'll just pass in a String. And now, if we come back over here, obviously, our left arm selector isn't going to be working because it's a string, not an array of parts. But if we take a look in our browser, you can see that we have a validation message here because we passed in an invalid data type for the parts. And you can see right here, it says, Expected Array, but got String. So we now have type checking that can help us identify problems during development. So let's go back and fix that, and then we can also make our props required. So here, I'm going to, in addition to specifying type, specify required: true, and then we'll do that for position too because it's also required. And now, over here in our RobotBuilder, let's not pass in the left position. So if we come back over here, let's clear this and refresh. Well, you might be surprised to see here that there is no message. There's no validation error, and that's because if we look at what we passed in, we didn't pass in nothing. We actually passed in an empty string. And so because we are providing a value for position, it's not triggering the validation message. If we remove this altogether, however, and come back over here, there, now you can see it's saying that there is a required prop that's missing. But what if we actually do want to ensure that users cannot pass in an empty string here? Essentially, we want to indicate what are the valid values for position. So we can do that with a validator function. So in addition to specifying that position is required, we're going to supply a validator function, and that is a function that takes in the value that's being passed into the prop. And then we just want to ensure that it is one of these values, left, right, top, bottom, or center. So basically, I just want to make sure that the value is included in that Array, or that this Array includes the value that's passed in. Now, because we are passing in an empty string, we should now be seeing an error over here. And sure enough, if we refresh here, you can see that we are getting on error that the custom validator failed for a prop position. Cool. So let's go back to our RobotBuilder and pass position back in here. And then if I clear this and refresh, you can see we're not getting that error anymore. And our robot looks great so awesome. We're now validating the props that are being passed into our child component. Next, we'll take a look at how to pass data back to a parent component from a child component.
We now have a fully functioning PartSelector, and we're passing data from our RobotBuilder down to the PartSelector via its props. However, notice when I click this Add to Cart button that I'm getting weird results down here in the cart. This is because our RobotBuilder, which is the parent component, isn't aware of changes that are happening inside the PartSelectors, which are child components of the RobotBuilder. If we look at the code, you can see that the addToCart method expects there to be a selectedRobot, and in our data function up here you can see that we are initializing each of the parts in that selectedRobot to an empty object. And before we extracted the PartSelector component, each of these parts was getting updated whenever the user selected a new part, but now that selection is happening inside our PartSelector child components. So when we click on each of these buttons, the RobotBuilder has no idea that anything has changed because these buttons are inside the PartSelector component, so we need a way to tell our parent component when a different part is selected. Let's take a look at how to create events in the child component that the parent component can bind to. This will allow the parent component to react to changes in its child components, and it will allow us to pass data back to the parent component from the child component. So over here in our PartSelector component, down here in the methods you can see that we have these two methods, selectNextPart and selectPreviousPart. We want both of these to emit an event that parent components can listen to by binding to them. This is surprisingly simple with Vue. Each Vue component has an emit function available that we can call to emit an event. So right here in the selectPreviousPart function, down here at the bottom I'm just going to call this.emit, and then I'm just going to pass in the name of the event that we want to emit. So let's emit the event partSelected. This now means that a parent component can bind to a partSelected event when it creates the PartSelector component. We'll see how to do that in just a sec. First, when we omit this event we want to be able to pass along some data with it. It won't do us much good to let the RobotBuilder know that a part was selected if we don't tell it which part was selected, so let's pass along the currently selected part like this. Remember, we have a computed property up here on our component called selectedPart, so we're just using that here to pass it along. Alright, and we're going to also want to emit that inside the selectNextPart function, so let's copy this and paste it here. Okay, cool. Now let's go see how to bind to this in the parent component. So over here in our RobotBuilder, up here we have our PartSelectors, and we'll start with this one here. We just want to bind to this new partSelected event. Binding to a components event is done the exact same way you bind to any other elements event, with the v‑on syntax, or the @ shorthand syntax. So we're going to bind to partSelected, and then we could bind that to a method on our component like this, but actually I think that this is going to be simple enough. I just want to handle it inline like this. So this is a function that takes in a part, and then this is the PartSelector for the robot head, and so I want to set the selectedRobot's head to this part, so we'll do that like this. So remember this selectedRobot is an object that we already have on our component, and we're just setting the head to this part that's being passed in. And that's it, easy peasy. All you have to do to pass data to a parent is emit an event in the child component and then bind to it like this. So let's go ahead and do this for the other PartSelectors. So this is the left arm, this is the torso, this is the right arm, and this is the base. Okay, so that's it, the RobotBuilder is now aware of the selected parts because we're binding to the partSelected events and setting the selectedRobot parts. So if we go back over to our browser now and select each of these parts and then click Add to Cart, you can see that that works just fine because the RobotBuilder is now aware of the data that's being passed back to it. But actually, we still have a small problem here. If I refresh this page and then click this Add to Cart button without changing anything, you can see that we're still getting our broken state. So it doesn't work when the page first loads, but if I change all of the parts and then click Add to Cart, you can see that it works just fine. This actually makes sense when you remember how we implement the events. If we go back over to our PartSelector, you can see that we only emit the events when the selectNextPart or selectPreviousPart functions are called, and these are only called when the user clicks the button. So when the page first loads, the PartSelectors haven't emitted these events yet, and so the RobotBuilder doesn't have any selectedParts. This is actually a great opportunity to tap into a component lifecycle method. So let's add a created function, and then let's emit the partSelected event when the component is first created. But instead of copying this line again, let's create a new function called emitSelectedPart. And then we will move this up into here, and then right here we can just call this.emitSelectedPart. And then we'll do the same thing down here, and then in our created event we'll also call it here. So now the currently selected part will get emitted when this component is created in addition to when clicking the buttons. And you can see up here that in our data function we initialized the selectedPartIndex to 0, so when the selectedPart component is created, the first part in the list of parts that's passed in is always the selectedPart, so we have a default selectedPart that will get emitted whenever this component is first created. So now if we go over to our page here and refresh, you can see that I'm not going to change any of these parts, and I'm going to go ahead and click Add to Cart, and and now it's working just fine because those events got emitted. So this is working great, but it's a little bit hard to see the events as they flow in as we click on the buttons, because we don't see that the events are working until we click the Add to Cart button. For that reason, in order to see this a little bit better, let's add a little preview image up here above this Add to Cart button that shows a preview of what our currently selected robot looks like. And as the events are emitted from the child components, you'll see the parent component changing. So we'll grab the HTML for that preview element over here in our GitHub repo in src, build, there is this RobotBuilder‑preview.html, so let's grab that. And then we'll come back to our RobotBuilder, and right here at the top of the template inside the content div, just above the add‑to‑cart button, we'll add that here. And then there's some styles for this, too, so let's go grab those, so that's this RobotBuilder‑preview.css. So let's copy that, and then back over here we'll just drop those in the bottom of our style section. Okay, let's go take a look at that and see what we've got. Okay, so you can see our preview is showing up here, so that's great, but the Add to Cart button is on top of it, so let's go fix that. So over here in our template, let's move our button into our new preview section. We'll just move that up here. And then down here in the css we just need to find the add‑to‑cart style, and we'll no longer need this right position, and we want to change the width a little bit to just 210 pixels. Now, let's go take a look at that. Okay, there we go. Now the Add to Cart button is below, and there's our preview image. And now that we have that preview image, we can see the events coming in as we change them by clicking the parts here, and you can see that the preview image, which is on the parent component, is changing as we change the parts in the child component. Okay, this is awesome, and this is a great example of how to emit events from child components to pass data back to parent components. I just want to take a short detour here to talk again about lifecycle hooks, because we have a great use case now. If we go back and look at our PartSelector component, you can see that right here we are emitting the selectedPart in the created lifecycle hook, and then down here in the selectNextPart and selectPreviousPart methods we are emitting it again. We could simplify this a little bit by using the updated lifecycle hook, and that is called whenever the data for the component is updated. So let's change this created hooked to an updated hook, and then let's come down here and just delete these so that they're not emitted. These aren't going to be necessary, because when we click the buttons we end up changing the data, and because the data is changed, the updated lifecycle hook will get called and emit our selectedPart. So now if we come back here, you can see that our data is not being updated, but if I click this, then updated is being called. But this isn't quite what I want, because we started out with a blank, and so we actually need both updated and created. We need those both to call emitSelectedPart. So now if I refresh, now as I change the parts, then you can see that the preview looks accurate. Cool. I just wanted to take that little detour to show you how lifecycle hooks work now that we had a better use case, and this is awesome. Now we've seen how to pass data to parent components with events, and we learned a little bit more about lifecycle hooks in the process. Next let's learn how to inject HTML or Vue content into a component.
Sometimes you want to create a component that you can pass content into and have it be displayed in the appropriate place within that component. Imagine, for example, a component that is used as a collapsible drawer that expands and collapses as you click on a header showing and hiding its content when clicked. In that case, you want a component to be reusable so that you can just wrap some part of your template in a collapsible component, and it shows and hides that content. To experiment with this, we'll do just that. Let's create a collapsible component that we can use to show and hide this preview image over here. So over in our RobotBuilder, we want to be able to use it like this. So around this preview content, I want to have a CollapsibleSection component and I'll wrap all of this content, the preview image, but not the Add to cart button in that CollapsibleSection component. And then basically, this CollapsibleSection component will have a header that says expand and collapse, and when we click on it, it will show and hide this content. So let's go create this component, and I'm going to put that in a shared folder since this is likely to be used by other components, and in there, I'll create a new file, CollapsibleSection.vue. And the template for this will be really simple, so we'll just type that in. So here is the template section of our component and it's going to have an outer div and then it's going to have an inner div that is the header, so we'll give it a header class, and then it's going to have a couple of spans in here, and those spans will say Collapse and Expand and these are going to be shown or hidden based on whether the collapsible section is currently open or not. So we will add a v‑if. And we'll say if it's open, then show Collapse, and down here if it is not open, then show expand. And then on each of these spans, we're going to bind to a click event, and we will just toggle open to not open and we'll do that for both of these. And then I just want a down and up arrow here, so I'm going to add these Unicode characters so that is an up arrow and then we want a down arrow here. Okay, so there is our header, and then basically, we want to be able to add right here our Toggleable content here. So this is where we want our injected content to appear, so we'll come back to that in a second. First, let's add our script block and we're going to export our component definition object, and this component will be named CollapsibleSection and it will have a data object which has the open variable that we're toggling with our click handlers up above. So our data object will just return a new object with open set to true, by default. Okay, and that's it for the component script and then we just need a few styles here and we'll have those be scoped as always and we just need to style our header and I want it to have a background color of gray and a little bit of padding, and we want it to have a pointer cursor. Okay, so now we have a component that has a header, and when we click on it, it toggles the open flag between true and false. Now we just need to know how to display injected content right here where we want our toggleable content. For that, we use slots like this. So this slot element is a Vue specific thing, and now that we've added it here, when we create a CollapsibleSection element like we have over in our RobotBuilder over here, this content that we put inside of our CollapsibleSection will be shown right here where this slot element is, it's really that simple. Of course, since this is a collapsible section, we want to hide this slot when it's not open. So we'll put a v‑if on here and only show it when open is true. Okay, so if we go back to our RobotBuilder component and we want to import this and it looks like I must have missed something here, yep, this CollapsibleSection needs to go down here actually. You may have already noticed that when I did it before. Okay, so if we come down here now and import our new collapsible section component, so I'm going to import CollapsibleSection from ../shared/CollapsibleSection.vue, and then we can use this down here in our components. And now that is available to be used. And so, now this CollapsibleSection component should be working. Let's go take a look at that. There we go. Now you can see that we have a header here above our preview, and actually, that's not working. I must have got my click handler wrong. Oh yep, here I meant to this to be = not open. So basically, clicking on these fans will toggle the open variable. So now let's come back over here. And when I collapse this, there we go. So our collapsible content is working, and you can see how we injected content into our collapsible content using a slot. And then one last thing to be aware of, this may be a little less common, is the concept of default content for a slot. So in our CollapsibleSection, inside this slot element, I can provide any content that I want, So I could say here div DefaultContent, and then this will be displayed if no content is passed in. So let's add another CollapsibleSection here just above this one, and we won't provide any content for that. And now, if we come over here, you can see that we have default content here instead of any data that was passed in because we didn't pass in any content here. And as soon as I pass in something here, then that will show instead of the default content. And that's all there is to working with slots, and you can go ahead and delete this, and that wraps up this module on inner component communication.
All modern front‑end frameworks provide some sort of routing mechanism for defining routes and navigating between pages in your single page app. Vue.js, of course, is no different. In this module, we'll explore how to do this with Vue. This will include adding routing to our app, linking to routed pages, styling links based on the active route, navigating from code, working with route params, using nested routes, using named views, enabling HTML5 history mode, and preventing navigation with navigation guards. Our build‑a‑bot app really needs some routing love, so let's go check it out.
If we take a look at our app component, you can see we're importing the HomePage component and RobotBuilder components down here, but without routing, the only way we've been able to see each of these pages is by replacing this element up here. What we really want to do is make it so that we can get each of these pages and others by changing the URL or by clicking on links. In order to do that, we'll need to install routing. If you remember, when we first created our project with a Vue CLI, we had the option to select routing, but we opted not to. However, adding routing to an existing project is really quite easy. First, we just need to install the vue‑router package. So over in my terminal, I'm going to stop my server from running and then I'm going to npm install vue‑router, and then I'm going to save that. Okay, and then let's go ahead and start up our server again, and then back over in our app, we just need to have a place where we can define all of the routes for our application. So let's create a new router folder, and inside there we'll create a new index.js file. Okay now, inside here, we're just going to import Vue, and then we just need to import the router, and we'll import that from vue‑router that we just installed, and now we just need to tell Vue to use the router like this. So now Vue is aware of the fact that our application is going to use routing, and then we just need to export a new router, and then we just need to define the routes in here. So we pass into this a router configuration object, and then it has a routes property, which is an array. Okay, now we're ready to add our first route. This will be a route to our homepage, and it will look like this. So, it's an object that has a path property, and this is the URL that when Vue sees that this is the URL in the browser, it should navigate to this route, and we'll give this route a name, we'll call it Home, and then we need to tell it which component to load when this route is navigated to. So we're going to tell it to load our homepage component, and then we'll just have to import that up here. Okay, so this is our first route. This just says when I navigate to this URL, then display this component, and then this is just a name that we're giving to our route, and we can use that in various places in our code. So this is pretty simple, and while we're in here, let's go ahead and create a route to our RobotBuilder, too. So, let's copy this, and we'll add another one, paste that in here, and the route for our RobotBuilder is /build, and we'll name this route Build, and then we just need it to render the RobotBuilder component, and we'll just have to import that. Okay, so this is our routes file all built out for the two pages that we currently have in our application. Now we just need to pull these routes in when we create our Vue instance so that Vue is aware of them. So, let's import our new router, and then we just need to add that to our Vue configuration object here. Okay, so now our Vue instance is aware of our routes. The last thing we need to do is tell our app where to display routed components. So over here in our app component, right here where we have been hard coding the component that we want to display, what we really want to do instead is display the component that matches our current route. We can do that by replacing this with the router‑view tag. So now Vue will display the component that matches the current route right here, and we don't need these imports anymore, and we don't need this components property anymore. Okay, cool. Let's go check this out. So if we come over to our browser and put in this URL, we're navigating to the root of the website, you can see that it loads the home page. You may notice, however, that it also added this hashtag and this part of the URL after the hashtag is currently what Vue is looking at for matching the routes, and we'll talk later in this module about how to get rid of that hashtag using the HTML five history mode. But for now, I can navigate to my pages by changing the route here. So now I want to navigate to /build, and you can see that takes me to my RobotBuilder page. Okay, awesome. So our routes are working great, but the only way we have to get around is by manually changing the URL. Let's go see how to create links to these routes.
Now that we have a couple of routed pages, let's see how to link between them. We'll start with this get started link right here. So over in our home page component, you can see that we have this Get started link right here, and it's just an anchor tag. In order to link to a route, we instead need to use view's router‑link element, like this, and then instead of an href attribute, we use a to attributes. So we're going to route to this URL, or this route, and I'm going to just say /build here. So I can put in a string here that represents the URL for one of the routes that we've added. And then I just need to add the correct closing tag here. And now if we go check this out, I can now click on this Get started link, and there we go, it navigated me to the build page. So that's working now, but how do we get back to the home page? Let's make this logo and text here a link to the home page. This will give us a chance to look at a little bit different syntax. So this is going to be in our app component, and that's up here in our nav section. So you can see here, here is my logo, and here is the Build‑a‑Bot text. So let's go ahead and add a router‑link around the logo and text. And then in our last example, we just used to and we bound that to a URL like this. Instead of using this syntax, let's actually use a binding here, and we're going to pass into it an object. And in this object, I'm going to specify the name of a route. So I want to navigate to the Home route here. And if you remember over in our router index file, each of our routes, we gave a name. And so we're routing to the route that has a name of Home, which is going to be this route here. So let's go check that out. So over here you can see that we have a link now for our text up here and the logo. It's also a link, So if I click on that, you can see it takes me to the home page. The only problem is that we don't want this link styling that we have here, so let's go add a class for that. So back over here on our router‑link object, let's add a class, and that class will be nav‑link. So notice that I could put a class on a router‑link element just like I can any other element. And then down here in the styles, let's just add that nav‑link class, and we'll set text‑decoration to none, and we'll tell the color to be inherited. So this will avoid changing the color of the link. Okay, so if we come back over here, there we go, now our link is styled, so I can go back to the build page and then back again to the home page, and this looks great. So now that we know how to link to our routes, let's go see how to style the currently active link based on the current URL.
Our PartInfo component is just currently hard coding the title and description here on the page. What we'd like to do is to be able to specify the part type, like heads, and the part index, and then use that information to look up that part and display its info. In order to do that, we're going to have to update our route to expect those parameters. And then we need to grab those parameters off of the route in our component. So let's update the route first. So first we need to update the URL for this route to add the params and you use it colon here followed by a variable name, so partType and then :id. So this route now expects a part type, like head, arm, torso, etc., and then a part ID. With this information, we'll be able to look up our part from our parts list and display its information. So now let's go over to our PartInfo component and update it to grab these route parameters. So first, we'll import our parts data, we'll import parts from data/parts, and now we'll need a computed prop. So let's add computed, and we'll have a part property that is a computed prop. So we'll return an object here, and that will look like the one down here in data. We're just going to cut this out of here and paste it into here, and now we can get rid of data. We don't need that anymore. And then this is what we want to do to look up the part from the route params. So we want to look up the part and get the title and description for that part based on the params that are passed in on the URL. So the route params are available to us on the components route property so that we can get the part type, for example, like this. We're going to say this.$route.params.partType. And then this partType right here needs to match the parameter name over here. So with that information, Vue can parse the part type off of the URL, and then we can get the ID in the same way. So id = this.$route.param.id. Okay, and this is red because our linter is telling us that we should prefer destructuring here. And so I can change this to look like this. Set partType and id, using destructuring, and we'll set it to this.$route.params. Okay, so now I can use this to look up the part. So instead of returning this hard coded object, I'm going to do a lookup on the parts array where the part type is the partType that I get off of the params. So remember that the parts object that we're getting, that we're importing, is a keyed object where the part type is the key, and then that returns an array of that part type. So then I'm going to do a find, and I'm going to find where the part.id equals the id from params. But actually, there's a problem here, and that's because the params that are pulled off of a URL are always strings, whereas the IDs on the parts in our data are integers. And so this triple equals actually isn't going to work because it's not going to find a part with an ID that is a string. So we can fix that by casting ID to be a number here. Okay, so now we have our router set up with params, and we have our components set up to use those params. The last thing that we need to do now is just pass the params from the PartSelector component. So over here in our PartSelector, down here in our showPartInfo function, we want to pass the URL parameters here for part type and ID. But we can't pass part params using the string syntax. We actually need to use the object syntax in order to pass parameters. So I'm going to change this to an object and use a named route where the name is Parts and then I can pass params. And then I'm going to specify here that the id param is this.selectedPart.id and the partType is this.selectedPart.type. Okay, let's clean this up a little bit. We'll bring this down here, bring this down, and we'll bring this down. Okay, so now we are saying navigate to the parts route and pass in the params, id, and partType, and we're going to get those off of the currently selected part. Okay, let's go check that out. So back over here. Now, if I click on one of these parts, there we go. Now we are navigating to parts/heads/1, and we get a description of that part, a Large Cyclops. And if I change this to this part, now it is the Friendly Bot head. Okay, before we wrap up on this, let's just demonstrate that this is also possible using links instead of code navigation by changing this link to be a router link rather than navigating from code. So back in our PartSelector up here, let's get rid of this click binding, and then let's wrap this image in a router‑link, and then we'll use a to binding, and let's grab the route expression down here from this function. So I'm going to cut this out of here, and then we don't need this function anymore. And then I'm just going to paste that expression in here, and I'm just missing a closing bracket here. There we go. Okay, so this is just the same as when we're navigating with code, so I can click on these and go to the parts page with the params. Awesome. So this is working great, and we're passing parameters between components using router‑link. But next, we'll see how to decouple our PartInfo component from the router by passing the route params as props instead.
One downside of using this route expression is that it couples this component to the router. What if we wanted to use this component as a child component somewhere else and pass the partType and ID as props instead of pulling it off of the route params. But we can make this work, both as a routed component and as a child component, by updating the route for this component to pass the params as props. So we can do that over in the route simply by setting the props property to true on this route. So now view will pass the route params that it identifies on the path for this route as props, instead of as route params. And with that simple change, we can come over to our part info component and add props for the partType and ID. And now, instead of getting partType and ID off of this.$route.params, we can just get it off of this, which is where the props are, and it's that easy to decouple our component from the route. You can see over here that that is still working just fine, but let's add a little bit of validation to these props. So we'll change the props to an object, and it will have a partType, and we want it to be of type: String, and it will have an ID, which will be of type: Number. But the problem with this is that when this component is used as a routed component, this ID will still be passed in as a string. So we need to allow either a number or a string. But just to be sure that nobody passes in an invalid value here, let's also add a validator, and then we'll just verify that this is a number. So we'll call Number.IsInteger and then we will cast a value to a number. Okay, so now we have some validation around our props. And if we come back over here, this should all still be working just fine. Great. Now we know how to use route params and how to pass those parameters in as props. Now let's explore nested routes.
In order to understand what we're going to build, in order to look at nested routes, let's take a look at the final result. So we're going to be building this Browse Parts page, and notice up here the URL is parts/browse. And when we navigate to that, it shows us a list of all the different parts. But then, as I click on each of the different part types, you can see that the bottom part of this page here is changing, but this top part that includes this menu is not changing. And notice the URL up here, as I click on each of these, is also changing. And so the URL is changing, but really all Vue is replacing is this bottom section down here. That is possible because of nested routes. So let's go take a look at how to build this. To get started, we'll create a new BrowseParts component. So I've created that here in the parts folder. Let's go grab the contents from that from our GitHub repo. So in the src folder in the parts folder, there is this BrowseParts.vue file. So let's grab that and paste that into here. So there's not a lot going on here, we have a bit of CSS and then up here we have a menu for the different parts. And then I'm going to need a new page for each of the different types of parts. So I'm going to create a new RobotHeads.vue and a new RobotArms component, and a RobotTorsos, and finally, a RobotBases. And then I'm going to get the contents for each of these out of our GitHub repo from their corresponding files also. So here's RobotArms, now I'll paste that into RobotArms here. RobotBases, we'll paste that in here, and then RobotHeads, and finally, RobotTorsos. And each one of these is really quite similar where you have a title and a description of the type of parts that we're dealing with on this page. And then you can see here that we have a v‑for that is looping over all of the, in this case, all of the arms and displaying the title and description, whereas here we're looping over bases, and torsos, and heads. And then in each one you can see that there is a data function that's returning the corresponding part. So we have a heads property on the RobotHeads component, and we have an arms property on the RobotArms component, etc. So now the trick is, how do we get each one of these pages to be viewed on the browse page underneath this menu whenever we click each one of the links for the different part types. Well, it starts with adding another router‑view. You'll remember that we have a router‑view already in our App component, and so we are adding a second one here. And now we can add routes for each one of these individual pages, RobotArms, RobotBases, etc. This is going to consist of a parent route for the BrowseParts page and child routes for each of the part types. So over here in our routes, we're going to create a BrowseParts route, and it's important that this goes above the existing parts route. And I'll explain that in a second here. So right here, I'm going to create a new route, and it's going to have a path of /parts/browse and a name of BrowseParts. And the component is going to be the BrowseParts component. And we'll need to import that, and it has to have the .vue extension on it. Okay, now back to why it was important where this goes. Notice that this parts path, where we're navigating to a particular part, that it has parts followed by a partType. Vue has no idea when we type in browse here that we're not trying to type in a partType. And so if we would have put this down below, it would have got picked up first by this route, and so the order is important. Okay, so now we have this BrowseParts route, and we're going to need to add the child routes now for the individual part types. We do that by adding a children array, like this. So the BrowseParts route now has child routes, and then we just need to specify the child route for each part type. So we're going to have BrowseHeads, and that's going to have a path of heads and resolve to the component RobotHeads. And let's copy this and paste it for each of the types. And this one will be BrowseArms, path will be arms, notice that this path is relative to the browse path. So the full path for arms is parts/browse/arms, and that will use the RobotArms component. Then we have BrowseTorsos and the RobotTorsos component. And then finally, BrowseBases, the bases path, the RobotBases component. So now let's go ahead and import each of those. Okay, so now we've created our nested routes. So we have our BrowseParts route that has child routes for each one of the parts pages. Now all we have to do is create links for each one of these on the browse page. So back over here in the BrowseParts page, I just need to add a router‑link around each one of these menu items. So a router‑link, and then I'm going to use a to binding, and here I'm just going to specify the name of a route. And notice here that I can just provide the name of the child route. Okay, and let's fix this so that it wraps a little bit better. Okay, now we just want to do a similar thing around the rest of these. Okay, so we should be good to go. So we added a BrowseParts route and it has child routes, and then we linked to each of the child routes here. Okay, so let's take a look. So instead of build here, we're going to navigate to parts/browse, and here we go. So now I can go to Heads, and Arms, and Torsos, and Bases, and you can see that the data for the correct corresponding parts is loading down below. Awesome. So pretty simple to add child routes. Now let's go take a look at one more place where we can use a router view, and that's with names you use.
We just looked at nested child routes. Now let's take a look at named views. You can almost think of named views as sibling routes. Whereas nested routes have a parent‑child relationship, named views allow you to have multiple router views on a single component, and the route for the component specifies which two or more views to display. To demonstrate that, let's add a sidebar to our app that will show up here on the left of every page. And each route in our app will be able to specify a different sidebar to display. Right now in our app component we have this main section down here that has a router‑view in it. Let's also add an aside that will show to the left of this main section. So we'll give that a class of aside. And we'll wrap both of these in a container div. And let's go ahead and add those classes. So we have a container, and it's going to be display flex with a margin of 10px auto 0 auto. And we will set justify‑content to center. And then we need an aside class, and it's going to have a 30px padding and a background color that is gray, 100px width, and a min height of 300px, which matches the min height of our main content area. Okay, as we add these asides to our site we're going to have to make a couple of other small CSS adjustments in order to fit those in. So, on our main element we're going to remove the margin auto, and we're going to change its width to 964px. And then we're going to change the width of the header to 1184px. Okay, that should take care of the styling. So now up here in the aside we'll go ahead and add another router‑view. However, so that we can target this router‑view let's give it a name. So we're going to add a name attribute, and we will name it sidebar. And we can leave this one without a name. And by default, if we don't give it a name it has the name default. Okay, now we need some content to display in this sidebar. So, let's create a sidebars folder. And in here I will create a SidebarStandard component, and I will also create a SidebarBuild component. So the build page will have its own sidebar. And our standard sidebar is just going to look like this. So if you're following along, go ahead and pause this if you'd like to type it in. And the only content up here is just the text Standard Sidebar. And we have a sidebar class here that is giving it a font size of 50px and rotating it 90 degrees. And that's really all that's going on here. And then let's copy and paste this into the build sidebar And we'll just change this to say Build Sidebar, and we will name it Build. And, just to make it stand out a little bit, let's give it a green font color. Okay, now let's come over to our routes. And up here on the route for our HomePage we're going to change this component property to components, plural. And then we'll change that from a simple property to a keyed object. And this will have two properties, default and sidebar. Remember that these names now correspond to the names of our router‑views. This one has a name of sidebar, and this one has a name of default. So, when our home page is displayed we want the default router‑view to display our home page. And then we want the sidebar router‑view to display our standard sidebar. And we'll need to import that up here. And then let's do the same thing for our build route, except that the default router‑view will display the RobotBuilder. And in this case, we want to display the build sidebar. So let's import that also. Okay, cool. So now, because our main app component has two router views, we have to specify them both, or we can specify them both, and we can indicate in the route which content to display in each one. So, let's go take a look at this. Oh, and it looks like we have an error in our CSS. So let's go back to our app component, and down here in the styles I'm missing a colon right here it looks like. Okay, let's check this out. Okay there we go. So we are on the build page, and notice that on the build route that I have my robot builder and I have the green build sidebar. And yet if I go home I have a different sidebar. This is the standard sidebar. And notice that if I go to the parts/browse page that there actually is no sidebar here, or I should say there is no content in that sidebar. So, this gray background is coming from the aside in our app component which exists right here. So we have the aside here, and that has a gray background, but there is no content in it because we didn't give it a router‑view on that page. So our route is not specifying a component for the sidebar router‑view and so it's blank. So, it is optional to provide data here, but this is how you use named views in order to specify different content for different parts of the same page. Next, let's go take a look at how to get rid of this hash sign in our URLs.
All of our URLs so far have had this hashtag in them. That's because we're not yet using HTML5 history mode. So let's turn that on, and then we'll talk about a challenge that is presented by using it. So all we have to do to turn it on is to come over to our routes, and in our router configuration object, we just need to set the mode to history, and that's it. Now if we come over here and start navigating around our site, you can see that there are no longer any hash signs in our URL. I really like this approach because the URLs look more professional, but there is one consequence to this that you need to be aware of. Imagine somebody comes to this /parts/heads/1 URL, and then they bookmark it, and then they come back, they close the browser, and they come back later, and they click on that bookmark. Or let's just say they open a new browser and they type in this URL. Well, this works locally in development because behind the scenes we're using webpack‑dev‑server in order to serve this, and it can handle this. But imagine you have this reading on your server and a user types in this URL or clicks on a bookmark to this URL. It's going to request this full URL from your server, so it's going to request /parts/heads/1 from your server, and your server doesn't actually have a document at that location. It has an index.html and some other files that are used for serving up your Vue.js single page application. And your Vue.js application knows how to serve this URL, but your server doesn't actually know how to use it. And so, you would actually get a 404 when you try to run this if you have not configured your server correctly. And that is not the case if you're using hashtag routing because only the part before the hashtag is sent to the server. Everything after the hashtag is handled client side. And so, this is a problem only when you turn on history mode routing. So if you don't configure your server correctly, all of these URLs that people deep link to will 404 when they try to hit them. Basically, you need to set up your server to always return the main index.html file whenever you load any URL like this. The way to do this varies depending on which web server you're using. So the Vue documentation contains some instructions for some popular web servers. I'll talk more about this later in the Deploying to Production module in the clip on handling deep linking. I'll demonstrate how to do this with Express. But just be aware that when you turn on the HTML5 history mode that this is a concern that you have to take care of on the server. Next, we're going to take a look at navigation guards and how we can prevent users from navigating to or away from certain pages.
Navigation guards allow you to either prevent a page from loading or prevent a user from leaving a page based on specific criteria. We'll take a look at both of these scenarios. Let's start with a beforeEnter guard. To demonstrate this, let's prevent this PartInfo page from loading if the ID that's passed in is not numeric. You might think that we've already taken care of this when we added a prop validator that required the prop to be numeric. But this doesn't prevent the page from loading. It is more of a dev time warning that just throws errors in the console if the ID isn't valid. We'd like to actually prevent navigation from happening. Notice that if we come over to our PartSelector and change ID so that instead of passing in a valid ID, it's just passing in a string, you can see that if I then try to navigate to this, that it actually does navigate. You can see my URL has changed, but I just have a blank page. This is not ideal. What I'd like to do is to prevent the navigation from occurring completely. So let's go back here and change that back. And let's add a route guard to prevent this. So we can add that on a component or on a route. Let's see how to add one on a route. So we'll come over here, and right here in our PartInfo route, I'm going to add a beforeEnter function. And that takes three parameters, the route being navigated to, the route being navigated from, and a next function. And if we want to allow the navigation to proceed, we call this next function with a value of true. If we want to prevent the navigation, then we call it with false. So inside here, we want to prevent navigation if the ID is not numeric. So to get the ID, we can get that off of the to route, and then on there, there is a params object, and that should contain the ID parameter. So, basically, if this is a valid integer, we want to allow the route to proceed. So I'm going to set a variable or const isValidId = true if the number is a valid integer. So we can do that like this, Number.isInteger. Then we cast this to a number, and now isValidId will be true if the ID param is a valid integer. So now we just need to call next, and we need to call it with false if the ID is not a valid integer. So we can just pass in isValidId here, and then if that's false, it will prevent the navigation. Otherwise, it will allow it. So this should still be working normally. Now I should be able to navigate to each of these parts, and that's working fine. But if we come back over to our PartSelector and change this again to a string, and then try to navigate, you can see I'm clicking on these, but nothing is happening. And that's because our route guard is stepping in and preventing the navigation because the ID is not valid. So before we forget, let's go undo this and put that back how it was, and you can see how creating this route guard was super easy, and it can be really handy. We could have defined this on the component instead, or we could even define it globally. In the next clip, we'll create a beforeLeave navigation guard to prevent the user from leaving a page, and we'll do that in the component so you can see how that works.
Sometimes we want to prevent or warn a user before they leave a page. For example, imagine I spend some time configuring the perfect robot that I want here, and then I click the home link up here to navigate away. This could cause me to lose all of my progress on the robot I've built, so I may want to warn the user before they lose their progress. Let's create a beforeLeave route guard to handle this. This route guard is going to need some information from the component. So we'll create this one inside of the component rather than on the route like we did with the beforeEnter guard in the last clip. So over here in the RobotBuilder, we're going to add a beforeRouteLeave function right here, and notice that this has the word route in it. Well, it's supposed to. Now it has the word route in it. So this beforeRouteLeave, and we could add beforeRouteEnter here, but you may remember that when we were adding this on our route as opposed to in a component, we actually didn't have the word route in here. So this is different when you're adding it on a component. So we want a beforeRouteLeave guard. And again, this has the to, from, and next parameters. And basically, we want to warn the user, if they try to leave the page without adding their robot to the cart first. So let's add a property in our data function down here that will track whether they've added it to the cart. So we'll create an addedToCart property and we will default that to false. And now, up here in our route guard, we will check to see if that's true. So if this.addedToCart, and if it is true, we will allow them to leave the page. So we'll call Next, passing in true, which will allow the navigation. Otherwise, we'll use a confirm dialogue to ask them if they want to leave or not. So I'm going to set, I'm going to say const response =, and then we're going to use confirm, and here we're going to say, "You have not added your robot to your cart; are you sure to want to leave?" So if you're not familiar with confirm, it will pop up a dialog, kind of like an alert, but it has an OK and a Cancel button. If they click OK, it returns true, otherwise a returns false. So if they click OK, then they're saying that they want to leave, and so we will pass response into next. And this is all we need. However, our linter is getting angry, because it doesn't like us using confirm. Generally alerts and confirms are not a great idea, but this is good for demoing this, and it's not bad to use it in the cases where it makes sense. And then we're also getting an error, because we're using a global function, so we're just going to disable those alerts here. So I'll just put in these two ESLint directives here, and now it's stopped complaining. There's just one last thing that we need to do here. Remember, we're checking to see if addedToCart is true, and it's always going to be false right now, because that's what we defaulted it to. So down in the addToCart function, once they've added it to their cart, then we're going to set this.addedToCart = true. Okay, let's go check this out. So when I first load this page, if I try to navigate away, I should get a warning. There we go, "You have not added your robot to your cart. Are you sure that you want to leave?" And if I say Cancel, then it does not navigate away, but if I say OK, then it does navigate away, so that's working great. And if we go ahead and configure a robot and add it to our cart, then addedToCart is set to true, and now we can navigate away without a warning. Great! So now we know how to use route guards to prevent users from leaving a page.
In this module, we learned all about routing in Vue, including adding routing to our app, linking to routed pages, styling links based on the active route, navigating from code, working with route params, using nested routes, using named views, enabling HTML5 History Mode, and preventing navigation with navigation guards. In the next module, we'll learn how to manage state with Vuex.
Welcome to this module on Managing State and Server Communication with Vuex. In this module, we'll explore how to reliably handle data in a Vue application using Vuex. Vuex helps us by creating a central store where we can store and retrieve data in a way that works well with change detection across an entire Vue application. At the heart of Vuex State Management is the Vuex Store. The Store provides not just a storage mechanism, but an interface for mutating data, requesting asynchronous mutations to data and managing complex logic for fetching computer data with getters. The interface it provides ensures a shared data store for your application that will work well with Vue's change detection. At the heart of the Vuex Store is the shared state tree. This is essentially a single object that contains all of the data for your entire application. When you first create your store, you provide an initial state object with reasonable defaults. This is important to Vue's change detection. We'll talk more about that later, but just know that the store contains a single state object that can be accessed throughout your application. Once the store is created, if you want to make changes to data in the store, you do so by committing mutations. Mutations are synchronous and shouldn't be confused with actions, which are asynchronous. You might wonder what the purpose of a synchronous mutation is. Why not just change the state directly? Essentially, by telling the store to commit a mutation, it keeps everything within the store consistent and working well with change detection. If you want to kick off an asynchronous call that will eventually mutate the store, such as making a call to an API to fetch data, that's where actions come in. Actions contain asynchronous calls using constructs like promises or async/await, which when finished, commit mutations to the store. Actions and mutations work hand in hand to make asynchronous calls and then mutate the state. Sometimes when you want to access data from the store, you want to perform some complex calculation logic before returning the data. For example, you may have an array of unicorns in your store, and perhaps it's common in your application to want to retrieve only unicorns who can heal fatal wounds Well,