Transcripts
1. Introduction: Hello and welcome to Shopify Theme Development with Vue.js. I'm Christopher Dodd. I'm a freelance web developer and top teacher here on Skillshare.com, covering all things web development and online freelancing. In today's class, we're going to learn how to integrate the JavaScript Framework Vue.js into the product and cart pages of a Shopify theme. This class is ideal for those of you who have taken some of my other classes on Shopify theme development here on Skillshare.com and are ready to take their theme development skills to the next level. With Vue.js, we're able to bring Shopify data structures onto the front end and allow for the user to live update elements without reloading the browser. Using Vue.js over other JavaScript libraries like jQuery, we can create a more optimal experience for both us as developers, as well as the end-users of our e-commerce store. If you're ready to get started, click on to the next video and I'll see you on the inside.
2. Required Knowledge: Today's class when using Vue.js inside Shopify themes is an advanced topic and therefore, you will require some prerequisite knowledge in order to understand and get the most out of this class. Before taking this class, you should understand the difference between front-end and back-end when it comes to web development, this is also known as client-side and server-side. If you're not familiar of why we would need to transfer data from Shopify Liquid into JavaScript, for instance, then I recommend you take a look at Lesson 4 of my previous Skillshare class, Understanding Web Development. Secondly, you're going to need to know JavaScript. If you need a refresher, you can check out my Skillshare class, Web Development Fundamentals: JavaScript. Third, you should have at least some familiarity with the Ajax API and Shopify. In this class, we'll be plugging it into view and using it to update our cart on the fly. If you'd like a refresher of the Ajax API, check out Lesson 8 of my class, Shopify Theme Programming. Of course, last but not least, you should have some general experience with Shopify theme development and writing Shopify Liquid code. If not, I definitely recommend my two previous Skillshare classes on the subject, Shopify Theme Development and Shopify Theme Programming. Other than that, even if you are new to Vue.js, you should have enough knowledge to continue on with the following videos.
3. Why use Vue: Before we get started, let's talk about why we would want to use Vue inside of Shopify themes. Put simply, Vue.js is handy for any areas on our Shopify website that require or encourage user interaction outside of simply navigating to new pages. Let's think for a second about how a user might commonly interact with an e-commerce website. We can break it down into three main behaviors. One, browsing or finding specific products to buy two, adding and removing products to and from their shopping cart, and three, taking those products to the checkout and completing their order. On the point 1, browsing or finding specific products to buy, most of the time this happens via the navigation. You simply click on the collection or category you wish to browse and you're presented with a list of options. This does not require interactivity beyond navigating pages. But what about when you reach the product page and you're presented with some options to modify the product to your liking before adding it to the cart. Here, when you update your selection, the product image, the product price, and other details might change. Preferably, the user should not have to navigate to a new page or refresh the page in order to see those minor changes. Secondly, after a user has finished browsing and adding items to their cart, maybe they'd like to remove products from their cart or add additional units of the same product. Again, it would be preferable for the update to be recorded instantly rather than having to wait for a page refresh. Luckily, both of these interactions we can handle on the front end using JavaScript. But using JavaScript or jQuery on the framework at all, we're going to have to specify which sections of the page are intended to change and in which situations. This results in us having to write messy codes, both update the data and then make it so that the updated data is then reflected on the page. Vue.js solves this problem for us via something called reactivity. I encourage you to read about reactivity in depth on Vue's website, but for now this is how I will explain it. Put simply, Vue allows us to create a data structure and insert dynamic values onto our front-end HTML structure. When the data changes, so do the values on the page. Therefore, they are considered reactive. When writing Vue.js, we don't have to explicitly tell the user interface to update, instead, the user interface will react to whatever the data bound to that element has updated to. Let's put it in terms of the two areas where we'll be using Vue in today's class, the product page and the cart page. On the product page, if the product has more than one variant, then some selection from the user is required. The selection then determines the current variant, which may carry with it a different price, image, and/or stock level. On the cart page, most if not all of the page content will come from what is currently stored in the cart, such as the various items in the cart, their images, titles, prices, and the combination of those prices, which make up the total price. When an item or unit is removed from the cart, this produces a trickle-down effect to the items array, the line price of the item, and/or the cart total. In Vue.js, we can open up our developer tools to clearly see our data structure and how the values on the page are kept in sync with the structure without having to manually write lines of code to update the values as seen on the page. To summarize, Vue.js is most handy in these environments, these situations in which you don't want to have to refresh the page each time an update occurs. On e-commerce stores specifically, Vue.js is particularly handy when dealing with variant selections and shopping cart interactions.
4. How to Use Vue on Shopify Themes: Vue.js is a framework that can be used to create a single-page web application, or it can be dropped into any single webpage. The Vue instance, as it's called, plugs into your existing HTML structure, and through the use of custom tags, attributes, and template syntax, enables binding of data and events from the HTML to the Vue instance. In today's class, we're going to create two Vue apps or instances, one One the product page and one for the cart page. For the product page, we're going to use Vue to track the currently selected variant. This will be determined by the combination of options selected by the user via this form. We'll import the list of variants from the Shopify back-end and use the values from these drop-downs to determine the current variant. For the cart page, we're going to import the cart data from our Shopify back-end into our instance using the cart AJAX API. We're also going to import a specific collection to offer upsells below the cart, as well as add transition loading states for the add to cart buttons and the cart table given that we will be making asynchronous calls to the AJAX API. That's the overview of what we're going to use Vue for in our Shopify theme. Note that Vue instances and Shopify templates have a one-to-one relationship here. As we start to build and then finish looking at Vue, it should start to become clearer why we're doing it this way. At the conclusion of this class, you might want to explore what other templates in your Shopify theme might benefit from a Vue instance. An example I can think of off the top of my head is the collection template. The Shopify store is with a lot of individual products, you might want to create a more sophisticated filtering system where the user won't need to refresh the page after adding or removing different parameters to filter a list of products. This would involve a lot of data being updated based on user input and would make a good opportunity for using Vue. For now, let's focus on the two most common areas where Vue.js makes sense on most Shopify sites, which in my opinion would be variant selections on product pages and cart interactions.
5. Setting Up Our Project: We're going to start off the screenshare portion of this class inside my usual testing ground for Shopify themes, Chris Testing Shop. Obviously, you're going to need a Shopify store to work on. This is the case in any of my other Shopify theme development classes. Keep in mind here, guys, that we are focusing on a very particular area of Shopify theme development using Vue.js. We won't be covering things such as CSS, how Shopify works, or the other things that go along with Shopify theme development. It's expected that you know how to use Theme Kit. It's expected that you understand CSS, HTML, a little bit of JavaScript. We're going to be focusing specifically on Vue in this class. That's why I've created a starter project for us to work through in this class, as we don't want to build a theme from scratch, but we also want to use a theme that is as simplified as possible. The way we're going to access this theme is by going to my GitHub account. So github.com, and I believe, it's just "Christopher Dodd". Here we go. Inside Popular repositories, you can see Skillshare, but there is a new one I've created here called "vue-class-theme". We'll head back to the Skillshare repository in just a second. But what I want to do here is grab this "starting point" in my vue-class-theme repository here and bring that into our code editor, which for this class, is going to be VS Code. Of course, you can use whatever code editor you want, I just prefer to use VS Code. It's free and it's powerful. Why not? I'm going to open up VS Code, click "New Window" here, and run a New Terminal. The way to get this repository is to grab the URL here and then "git clone". There we go. That will be somewhere in our file structure, wherever we've got our terminal set up to go. For me, it's going to be here in my user accounts. There you can see, I'm just going to move that onto the desktop just to make it a bit easier. Then now, I can open this up. I'm just going to open, head to that folder, and here it is. Now, as is the case with all Shopify theme development, we're going to have to set up a config.yml file. I've covered that a few different times in the different YouTube videos I've done. It's been covered in the first class on Shopify theme development here on Skillshare, so we're not going to go through Theme Kit. What I have got already inside the shared folder here, I've got my config.yml. I'm just going to bring that in. Of course, the config.yml file has all the authentication details and the theme ID in order to actually log into the store. I'm going to close that one down once we have that. The next step for us is going to be uploading this theme to our Shopify storefront. What I'm going to do for that is zip this up, do Compress on a Mac. Now it's zipped up, going to go back to my testing shop, click "Upload theme". Select the zip file. Hit "Upload". That'll take a little bit of time to upload and then we'll see it come up. I'm just going to remove that capital V. Now, we should be able to click "Preview" and see a preview of our theme. On the homepage, there's nothing, but what I've got here is two links, one to shop the tee or shop shoes. Now, we'll talk about the store data in just a second, but let's have a look at what we've got so far. We've got our t-shirt here and then we've got our collection page where we have a range of shoes. Now, of course, this is all based on store data. I mention this in pretty much every class, this is pulling from the information you have inside your store. You're not going to have the exact same information as me, but I've tried to make it as simple and as easy for you to get started and get this up and running on your store as possible. What I've done is I've exported all these products. Just have to go back to GitHub, go into the Skillshare repository. Here shoes and shirt CSV, if we import that into Shopify, then we're going to have all of these products in our store. Here's a little video of what it looks like when you're importing that information into a new store. Now, you should have your theme uploaded and you should have these products uploaded as well. If you want to follow along, you can just use whatever products you have in your store right now if you want, that's up to you. But now we've got the theme here. Let's connect our Theme Kit. As I usually do, going to open up, customize, grab the theme ID of this theme. Go into our config.yml file, update the theme ID here. To confirm that that works, I'm going to open up a new terminal and run theme watch. Now, you can see we're connected to that theme and we're now watching for changes. The next step is getting our environment set up for Vue. But before we do that, I just want to familiarize you with the project as we haven't so far. It's a very simple one. This is based off a starter theme that's quite bare. You will see that there aren't a lot of assets, there aren't any snippets here. In fact, there are no sections either. This is a very simple theme. Obviously, if you have a production theme, you would have more folders and files here. We're just keeping it simple for this class. First thing I want to draw your attention to, is the theme.css file. I've already created these style rules. I didn't want to spend time in this class creating CSS, so I've put this all in a CSS file already for us. There's a theme JS file here. We can put in our general JavaScript for the theme in here, but we're using Vue and plugging in to store data. It's not really required for this class and we won't be actually using this file in this class. But this is an option as well for your general JavaScript. I've also got a site logo here. This is just Chris Testing Shop. No matter what your store is, if you're using this theme, you will have the same logo unless you change this file. I've got a trash icon for the cart page, and I've got this script here, which you can find here on GitHub if you wish. Otherwise, it's included in the project already. It's a way to do the money formatting in Shopify via JavaScript. Otherwise, you are stuck with Shopify liquid money formatting and that's not going to work when we're using JavaScript and Vue.js. That's your assets folder. I want to bring your attention next to the layout file. We've got a pretty basic layout file here. I can see I've included slick slider, which has been removed. That'll be removed for your version of the theme. Basically, what we've got is theme CSS included, that's the only extra CSS that we've got included. We're including this Shopify money script so it can format Shopify money values. I've included jQuery as well. JQuery is handy for doing our AJAX requests. Otherwise, if we use Vanilla JavaScript, the code ends up looking messy, and it's just so much easier to use jQuery. I've included jQuery there as well. Those are a few dependencies. We're going to need shopify-money to format the money values and jQuery for the AJAX requests. Again, we can use Vanilla JS, but jQuery is a bit nicer. That's about it for dependencies. Now, it's time to actually bring in Vue.js. The way we can do that as stated in the docs is, after our jQuery tag here, add in this script tag which brings in Vue. Now, if you're wondering where I got that from, it's in the Vue Documentation. This is a good opportunity now to show you where you can find the documentation for Vue. The thing you really need to be aware of is that in this class I'm going to be using Vue 3. There's three different versions; Vue 1, Vue 2, Vue 3. Maybe if you're watching this at a much later date, there's more versions after that, but at the time of recording, it is currently at Vue 3. Here you can see you're browsing documentation for Vue 2 by default. You need to click here for Vue 3 documentation. As you can see, the site looks pretty much exactly the same, but your URL here is different. Click on "Get Started". This is your reference for anything Vue throughout the class if you get stuck. As you can see down here, just the one before under installation, here is the script tag that I got. That's the exact same as what we've got here. The next thing we want to install is Vue DevTools. Trust me, this is so helpful when developing for Vue. Vue DevTools adds a extra tab here in our DevTools inside Chrome. I'm sure there's versions for other browsers as well, but I recommend you do use Chrome when doing this web development. We're going to add an extra tab here that will allow us to inspect our Vue data layer and also our computed properties and all sorts of things. It helps a lot with figuring out what data and things are happening in Vue while we're developing. For that, I'm going to look up the Chrome extensions store, Chrome Web Store. The thing to note here is if we're using Vue 3, then we have to use the beta version here. This is the one that works perfectly on Vue 1 and Vue 2, but we need to use the beta if we're going to be using Vue 3. Obviously, this will change depending on when you're watching this class. But currently to inspect Vue 3, we're going to need to use this version right here, the beta. I'm going to click "Add to Chrome", hit "Add extension", and you can see it has been added to Chrome. Now let's open up our theme and preview the theme. I'm pretty sure in order to get that Vue DevTools to work, we're going to have to create an actual Vue app. Says Vue.js not detected. I've included the package, but we haven't created an app yet. Just to prove that this is all working, what I'm going to do is go into the first template. We're going to be working on the product template and open up some script tags. Then inside here, I'm going to create a Vue app. Remember, in the theory section of this class, we talked about how we're doing a one-to-one relationship between the apps and the templates in our Shopify store. We're going to create an app called ProductPage to go along with this template. We're going to open up an object and then we're going to create an app from that object. Below this object, we're going to write vue.createapp, and then load in our object there. Then we're going to mount it to our DOM by specifying the ID of the elements. I've already set it here to product-page. Using CSS syntax with the hash, putting in the product page there. Now, guys, just to note here that this is Vue 3 syntax. If you're looking at examples online, the syntax might be different because that person might be using Vue 1 or Vue 2. Just keep that in mind. This is the newest syntax for Vue 3. If you're looking at old examples, it'll look a little bit different to this. But your point of truth here is you can always go back to the documentation, make sure you're on version 3. Then if we go down to introduction, you can see here that this is the same format. We might actually get an error if we don't include a data object. I'll just check that first. Let's go to our product template. Let's head over to console. Doesn't seem to be any issues so far. Head into the Vue tab, and here we go. We've got this root element here. There you have it, guys. We have just created our first Vue app. We can inspect it now in the Vue DevTools, it's going to be the final tab probably in your DevTools over here on Chrome. Now we're in the box seat to start creating our first Vue app. Without further ado, I'll see you in the next video where we build out this product page app and add some interactivity to this page. As you can see, we can change the variant here, but it's not changing it anywhere on the page. If I click "Add to Cart", it's just going to add the first variance. We need to fix that and we'll do so in the next video. I'll see you there.
6. Using Vue on the Product Page: In this lesson, we're going to refactor the product page here to use Vue.js. If we look at the code now, the content and all the dynamic values are coming from Shopify liquid. What that means is, that it's rendered on the server side once when the page loads. No matter what we do, the current variant is going to be small white because that's the variant that's selected on page load, and no matter what we do here to update our color, the color in the product image is not going to change, and that is a bad user experience, of course. Currently, we don't even have this setup to change the variant id. When we click "Add to cart" it's going to add the small white T. I'm going to clear my cart quickly by running fetch. You can do this as well. This is just the end point of the cart Ajax API to clear the cart. Just fetch cart clear.js, and that refresh the page that'll clear the cart. Back to this page right here. If we go into the code, we're rendering all of this Shopify liquid on the page, but of course we want to update certain things depending on the selection here in the variance selection. This is why we want to use Vue. The first thing I'm going to do is set up the data. We saw that before. We're going to have to create a data function within this object, and then inside this function, we return an object which holds our data. Let's think about some of the things we want to track in this app. Of course, we want to track the current variant id because that's what's going to get added to the cart when we click "Add to cart". I'm going to start with that current variant id, and the starting value of our current variant id is going to be equal to what we currently have here in our value. As I mentioned before, the value that's been inserted into this hidden field, and therefore, what gets added to the cart when you click "Add to cart" is the selected or first available variant. Let's move this value into here, our Vue instance, and that's going to ensure that the current variant id on page load will get set with that value. But then we can update it as we interact with our Vue app. Now what I'm going to do here is something very subtle but very important. I'm going to put a colon in front of value, and that's going to transform that field into a dynamic Vue field. Instead of putting an explicit value in there like here, the value is going to be one, we can put a variable name in there from our data object and it will update depending on what happens to this data. This is where the reactivity comes in. Here, we're going to put the variable name in here. The idea is that if this changes over time, this will also change automatically. But for now we should see the exact same results. I'll refresh the page. Let's go over to our Vue tab, and you can see here we get to inspect the data and we can see our current variant id. If we inspect the form and look for that hidden field, you can see that's the value. Actually what we can do is go in here and edit this on the fly. If I put this as 1, 2, 3, 4, 5, for instance, I've changed the value, and then you can see here it's changed in the DOM as well. You've got this data binding happening here. It's pretty cool. If I refresh the page, that value will get loaded in again. That's pretty cool. The next thing I want to capture, the different variants available for this particular product. The way I'm going to do that is by doing a liquid loop. This is not the cleanest code, but it's just the way we have to do it in Shopify liquid. I'm going to create a variants array here, and inside this array, I'm going to use Shopify liquid to give us our variants. We're going to put in a for loop here, variant in product variants. Basically what I'm doing here is transferring the data from Shopify liquid into our Vue instance so that we can interact with it inside of Vue, all on the front end without having to rely on Shopify liquid. For each variant in product variants, I'm going to have an object with a comma. Don't forget the comma, otherwise there won't be any separation between variants, and I'm going to copy across some of the data from Shopify liquid. First one, variant id, we're going to need to know if the variant is available. We can set that without quotations so that it can just insert as a Boolean value there, and then we're going to grab its option values. Within the three options that we have available to us in Shopify, what is the selection? If you're unfamiliar with any of these objects, you can always look them up on the Shopify liquid documentation. I might show you that right now just as a quick point of reference, Shopify liquid reference. If you're ever confused about what attributes you have on a particular object, you can just go into the reference, go down. Look at variant is the one we're looking for, and then you can see explanations of all these attributes that I'm putting in to our Vue data. I might just keep that one open just in case, and let's go back and build in the rest option 2 and option 3. Then I'm going to put in the variant price, making sure to filter that through the money filter in Shopify liquid, otherwise it's going to come out as sense with no formatting. Next I'm going to grab the image for that variant, and I'm going to add in an extra step here to check if the variant image is blank. Because in Shopify, if the variant image is blank, it still loads an image, it's just the wrong image. I'm going to create a variable here first, and you'll see what I'm doing in just a second, grab that image URL using all the filters available in Shopify liquid. Again, check the reference if there's anything here that's confusing to you. Then in here what I'm going to do for image is put in unless statement. Unless image URL contains no image, which is the image that comes up when there is no image set on that particular variant. I'm going to output this image URL that I've set in this variable just above, and there's no else statement there. If it is, a no image, is just going to show up as blank. Sweep. Now, just on the issue of images, what I'm also going to do is create a fallback image. In case the variant doesn't have an image, which is going to be a lot of instances, I'm going to just use the featured image of the product. I'm going to do fallback product image is going to be featured image, which is a variable we've set up here. I can take that featured image and then grab the URL of that featured image, making sure we put that in quotations. Now let's have a look inside our Vue DevTools, make sure we haven't made any syntax errors. Refresh over here. Looks like we might have unexpected token. Here we go, available. I've typed in varian rather than variant. Let's refresh the page. No JavaScript errors, head over to Vue. Here you can see we've got an array with all the variants and that data that was set up. We've got our fallback product image. We've got our current variant id. We can use all of this information now within our Vue app. We can bind it to elements on the page or we can use it in calculations. Now there's actually just a little bit more data that I want to add here, and that's to capture our selection here. We're going to just above variants, put in our variant selection, and we're going to bind this to our form. Make sure that the comma is in there. This is just option 1 is going to equal the selected value of our options with values, selected value. Here what we're doing, if we go into the documentation, we look at product. There is an attribute here called options with values, and it will return an array. Inside the array will give you the values and the option name of all the options. You have name and value here. You can have a look at that particular object here. Name, position, selected value, values. We're using the selected value of the first product option in our products. That's going to be option 1. Make sure to put that in quotation marks there. I'm just going to copy for the next two. For this 1, 2, and 3, we just need to update the position in the array and that should be all good. Now let's go back over to our page here. You can see we've now got our variant selection, which is small and white. We've got this variant selection here loaded into Vue on page load. But what we don't have currently is data binding so that when this updates, our data in the Vue instance updates as well. Let's do that right now. Let's go over to our code. I'm going to drag this down because we don't need that so big. What I'm going to do is bind these values to these selects right here. As we can see here, we're looping through the options with values attribute on the product, which for each item in the array gives us the name of the product option, the values that are available, and the selected value. We can use this logic to figure out what the current selection is and create a select in order for the user to change that. What we need to do is bind this value of the select to our data model here. The way we can do that is by adding an attribute here of v-model. We add this to the select because that's the one that's carrying the value. Within here we can start at the root of the data object. We're going to do variant. Actually, it might be worthwhile to have this side by side. You can see both sides. Here's our data object right here. Here is the HTML code. We want to go into variant selection and pull off the relevant option. I'm going to go variant underscore selection option. Then what I'm going to do to select the particular number that I want is use the attribute within this loop on the product option objects of position. Product_option.position. That's going to insert a number from one in here, and that's going to link it up with this. Now we've Vue model on our element. We can go back to the page over here, refresh. If I change one of these options, you can see it updates in the data. That's all well and good. We are updating our variant selection. But what we need to do is convert that variant selection into an actual variant. We've got our list of variants here. What we need to do is calculate what variant is currently selected by the selection that we have stored here. This is something we would need to do inside any Shopify store, whether we're using Vue or not. I won't spend too long going through it. What we're going to do is create our first method though. This is something new that we haven't covered yet, is to add a method. On our instance, we've got our data object here. But let's create a method which is basically a function on our Vue instance. How we do that is make sure we put a comma there and we define a methods object. Inside the methods object, we create our first method. I'm going to call it variant from options. Inside I'm going to look up the variant based on the variant options. Where I'm going do this is I'm going to copy and paste some code, and then I'm just going to explain them. What I'm doing here is using the find method on this variants array to find a match. When the variant option 1 equals the variant selection option 1, then that's a match on that particular one. We'll start checking to see if it's blank. Because if it's blank, then we shouldn't even look for a match by default is going to be true unless we come up against a false. Then at the very end, we're going to be returned with a match. That's going to return the find method here. What we're going to do with that value, if I create a new line, is update the current variant id to be equal to the variant that's selected in this find statement and take the ID of that. Now that we've created this method, we still need to hook it up to our element on the page. We're going to use a another attribute here in Vue.js, starting with the @ symbol and then writing the event that we're looking for. This is basically saying onchange. Then onchange, we're going to run variant from options. We've got the binding, and then when we change it, not only are we changing it here in our variant selection, we're going to recalculate the current variant id from our variant selection here, or here rather. Refreshing the page. I want you to notice over here. In our Vue instance, if I change the selection, you'll see that our current variant gets recalculated. If I select Medium black and I click "Add to Cart." We should now see a medium black t-shirt being added to the cart. Just going to run that Fetch command again to get rid of the cart. Let's go back to the shop Tee product page. That's all well and good now we've got our variant selection working. But what happens if each of our variants has a different price, a different sold out status, a different variant image. We want that to update on the page. Also, there's a thing in Shopify where the variant id gets stored in the URL. If you share that URL or refresh the page, the same variant is selected, so we need to add that as well. The three areas that we're going to do is the variant image, the variant price, and the history state in our URL. Let's head back to our Vue app. If we're going to actually find what the current variant image is, the current variant price and the current variant ID is, well, we already have the ID updating here. We're going to need to understand which variant is currently selected and grab that as an object from this variants array. If I go back to the data inside Vue, the only thing we have on the current variant is its ID. That allows us to look up the variant, but it doesn't actually allow us to access data attributes on the variant. What we're going to do is use this to look up the relevant variant object inside this array. The way we're going to do that is via something called a computed property. Computed properties exist in Vue.js because we don't want to put functions and logic inside our HTML structure. We just add an extra object here with our computed properties. Then we can treat the computed property as if it is a data attribute here and place it in our HTML. What we're going to do inside our computed property, we're going to create our own property called current variant. You'll see this reflected in our Vue DevTools in just a moment. Computed properties are basically just functions that return a value. We're going to do return and then we're going to look up the variant using the find method. Variant, and all we need to look for is the variant id equal to this current variant id. That should give us the relevant variant object from this array. Let's go back here, refresh the page. You'll see under data we've got computed now. If we look in current variant, you can see that we've got an object with the variant id, the options, the image, the price. If I go in here and change this, then this will update. If the price is different, that will update. See here, the image will update if it is supposed to be updated. This is all happening without us having to trigger a method because a computed property is reactive. If any of this changes, then all the computed properties will be recomputed. If we put it into a method, then we would have to recompute explicitly. That's the beauty of computed methods. We can tie together data and have that be reactive as well. Now that we have our current variant computed property, we now have access to our current variants image and price. Let's make sure our UI updates in response to that. I'm going to go back here, and I'm going to go up to, let's do the price first. I'm going to get rid of this, and I'm going to add an attribute on the element itself, code V-HTML. Anytime you see v dash, that is most likely a Vue attribute. I'm going to add V-HTML. Inside V-HTML, I can put in the attribute that we're looking for. It's going to be current_variants.price. Then up here for the image, I'm going to replace this. I'm going to make this dynamic by putting the colon in front and then I'm going to put in the current variant image. Now, if the current variant doesn't have an image, we're going to need to use the fallback. I'm going to use the double pipe syntax here. If this comes as false, null, or undefined, I believe the list is, then we're going to default to the value that comes next, which is going to be fallback product image. That's going to be very important on products that don't have any variant images set. Let's go back to our front end here, and let's change this to black. As you can see, the variant image updates it's not particularly smooth right now we're going to add a bit of a transition on that but as you can see, when we update the color, the variant image changes as well, changing that color. If we update the size, you can see that the price is changing. As always, you can use the Vue-devtools tools to check whether this lines up with the data, which of course it does. The third thing I wanted to bind is the history state, which we're not updating currently. That's important if you share this link with somebody, or you refresh the page. If I refresh now, you'll see it just defaults to the first selection. Let's create a method for updating history state as well. Inside methods object after this variant from options method, I'm going to create an update history state method. I'm going to put in the argument of a variant, and then here some code, "If history replace state". This is code I copied from a previous theme because it's going to be the same regardless of whether you're using view or not. I'm just loading it into a view instance on this occasion. I'll just type out the rest of the code and we can talk about what it's doing in just a sec. It's not essential that you 100 percent understand exactly what's going on here, obviously it helps if you do, but basically what we're doing here, if there is no "history replace state", and there is no "variant", then we're just going to return. But if there is, we're going to create a new URL. We're going to take the protocol, which will probably be HTTPS, then add these two slashes, then the host, then the pathname, then we're going to add in this query parameter of variant equals and then pass in the variant ID. Then we're going to update our Window history to the new URL. Feel free to copy and paste this code. All it's doing is updating the URL in a browser using JavaScript. Now of course, it's not going to trigger without us cooling it somewhere so what I want to do after the variant has been calculated from these options, I want to update the history state. In order to trigger a method from the same instance, we just do this.updatehistorystate, and then we've got that variant object so we can just place that in there. That'll find the variant ID from this object and place it after this query parameter in our browser. if I refresh the page over here, let's select a different variant and see what happens. Here you can see that our variant ID gets added or updated in our browser. If I refresh the page, I'll get the same selection. Those are the three areas that we want to be reactive. We want the price to react, we want the product image to react, and we want the history state to react. That's basically it for our Vue instance. The one thing I'm going to do before we move on is, this does look a little bit glitchy. I mean, it could be a better user experience so I'm going to use something that comes standard in Vue, which is transitions. If I go back to where that image is, and then I'm going to wrap the image in a Vue element called transition, I'm going to call it fade, which will give it the fade transition, and then copy that back in there. There are two things we need in order to make this work. One is we need the CSS. What this is going to do is add some classes to this element while it's changing. We need to actually put into our CSS what we want it to do when those classes are added. I'll just show you where that is in our theme. CSS, right here. It's going to add and remove these classes. We just need to define them in our CSS. You can find it on the examples of what to do though on the Vue documentation. If we go into "Transitions" "Enter & Leave", you can see this is basically the exact same code from the documentation so here is the reference. The other thing we're going to need is, we need to add a key to this element, because Vue is not going to know that it's changed even though this SRC has changed. It needs to know that the element has changed. One way we can force it to understand that is by giving the element a key. We're going to do :key, and then we're going to make the key equal to the exact same as here in the SRC. Now if I refresh the page, and let's change this attribute, you can see we've got this nice little fade between the different colors. How good is that? There you have it. We've got our products page here. If I select any variant, the price, the variant image, and the variant ID in the URL is going to update alongside it, or within a few milliseconds, and it's going to save the current variant ID based on the selection. When I click add to cart, if I have selected a large red t-shirt, it's going to add a large red t-shirt to my cart. As you can see here though, this has no interactivity, if I up the quantity, it's not going to work, if I click the trash, it's not going to work. These upsells down here aren't linked up. If you're ready to jump into the next lesson, we're actually going to connect this page up to Vue, and Vue is going to handle all of these interactions and sync all of that data. The cart page, of course, is a great candidate for Vue.js. In the next lesson, let's actually hook up this cart page.
7. Using Vue on the Cart Page: In the last lesson, we hooked up the functionality for the variant selection on the product page. Now if you change this is going to change all of the elements on the page that are linked to that selection. Let's up the ante now and work on the cart page. As you can see here, there's quite a few possible interactions. One, we should be able to update the quantity, so we can add more T-shirts to our order, add less T-shirts to our order, we should be able to remove a line item from the cart, clicking this trash button. We should also be able to add any of these up sells to the cart and have the cart live update. In this lesson, that's what we're going to build using Vue. We just refresh the page, make sure everything is in sync. Let's move on from our product template here and open up our cart template. In this lesson, we're going to have to refactor some of these loops that had been created in JavaScript, because we'll be updating these lists using Vue. It's going to be a lot more dynamic even more so than what we did in the previous product template. As we've done before, let's open up a script tag at the bottom of template file here, and using the same convention, I'm going to call this Vue app CartPage. Then I'm going to put that object into a create Vue app method, so Vue.createApp CartPage.mount, and I'm going to mount it to the element which has the ID of CartPage. Now, one of the biggest differences between this Vue instance in this template compared to the product template is we're not going to use Shopify Liquid to load in the data. The reason why is we have an API called the cart Ajax API that allows us to use Ajax request to gather cart information and update the cart. We actually don't need to use Shopify Liquid at all. I think it might be worth mentioning that we could actually use the Ajax API in our product template as well. Instead of this where we're creating the data objects using different Shopify Liquid tags, what we could have done instead, and I'll just paste in some code right now, is instead of this right here, just comment this out, so it's not distracting, instead what we could have done is started with an empty product object and then brought in the product information via the Ajax API as well. I opted against that, and the reason why is because we want the objects that populate on page load, this can cause additional problems, if this is empty on page load and that gets loaded, just makes it a little bit harder. But this is an option as well on your product page app, if you wanted to avoid using Shopify Liquid. I'll just put this code back to where it was before. Much the same concept as on the product page to the cart page, we could load in the cart information using Shopify Liquid. But in order to update the cart on the fly, we're going to need to use the Ajax API as well. What we're going to do is only work with the cart object via the Ajax API. In this particular app, we're not going to have to use Shopify Liquid. That's a big difference. As mentioned earlier in the class, I expect that you understand a little bit about the Ajax API. If you don't, there is a lesson that I'll link up in the description from a previous Skillshare class of mine. The Ajax API is a way for us to gather especially cart information via JavaScript. We don't have to actually use Shopify liquid. This is perfect for creating front end cart interactions, which we're going to be doing right here using Vue. Let's actually load in some cart data from the Ajax API right now. Like I showed you before with the product object, we're going to have to start with an empty data object or an empty cart object here. We know that inside the cart object they're going to be items. We can define a basic structure here, but until the Ajax request comes back, we can't actually populate this data yet. We're just going to start with cart items as an empty array. But what we're going to do on the created lifecycle hook is update this object right here. I just mentioned a new concept here, lifecycle hooks. It's basically different parts of the creation process and mounting process of a Vue instance, we can trigger certain things. Just like you would when you're writing, say, jQuery or any other JavaScript, you would wait for the page to load, for the DOM to load in order to execute some JavaScript. With Vue, we can plug into different what's called lifecycle methods. Just a quick look-up of what this is. If there is a search here, lifecycle hooks, we've got beforeCreate, created, beforeMount, mounted, beforeUpdate, updated. But basically the main common ones are mounted and created. What I'm going to do is as soon as the Vue instance has been created, I want to trigger that Ajax request and update our cart object. Let's actually go over to our cart page and let's add in that lifecycle hook right after the data method there. It's just a method on the root of the Vue instance, so we'll say when the instance is created, we're going to create a get request, and this is using jQuery. You should be familiar with this syntax. We're using jQuery to hit the cart.js endpoint, then we're going to pipe a.then, treat the response. We're going to JSON parse the response, so that's going to become a JavaScript object then. Then we're going to take that JavaScript object which is going to be our cart and then here we're going to set the cart object in the data of that instance to the cart object that is returned from the API. If I go over here, refresh and open Vue DevTools. You can see that the cart object from the Ajax API is populating our.data now. You can see so much information all from the cart Ajax API, we have all of that information now inside our Vue instance. If you ever want to check on the Ajax API request as always you can go into the Network tab, refreshing the page, here you should see it. It should be one of the first ones there, cart.js. You can see we're making this GET request status code 200. It's all good. This is what comes back. Awesome. We've now got our cart. What we want to do is use our card object inside Vue to render this part now rather than Shopify Liquid, because we're going to be updating the cart based on if we delete a product or we add a new product to the cart. There's going to be products added and removed from this list on the fly, and we don't want to have to wait for Shopify Liquid to do a page load in order to update this table. What I'm going to do is introduce some new things we haven't seen before in this class which is a loop, and some conditionals as well inside Vue. As you can see here, we've got if the number of items in the cart is greater than zero then we want to loop through them and show them. We don't want to do that with Shopify Liquid anymore. Let's refactor this to use Vue. What I'm going to do is get rid of that and use what's called a template tag. A template tag is basically a tag that's not going to be rendered in Vue, but it's just used for logic and loops. I could do a div here and wrap this all in a div and then do my v-if, but I don't want to actually render a div. I'm just going to div-template, and you'll see in just a second it won't render an item, but it will render what's in here if the if-statement is true. To put a conditional inside of our Vue HTML here, we can use v-if, and what I want to do is similar to how we just did it in Shopify Liquid. I'm going to look at cart.items.length and check if it's greater than zero. If so, we're going to loop through, else, what I'm going to do, end that template, create a new one for else, and all you do is v-else, and put in your cart is currently empty. I'll get rid of that. End if-statement there and refresh the page over here, it should look the same. But one thing you should notice actually is that when we refresh the page, there's a slight glitch before it appears because we're calculating whether it should show or not based on JavaScript. There is a split second where the cart object is actually empty. This is actually false for a split second before the cart information is loaded in, and then this becomes true. It's a bit glitchy, but we'll fix it up later in this lesson. Now we want to replace this Shopify Liquid for loop with a Vue for loop. For this we can just put the for loop directly on this element itself. I'm going to get rid of the beginning-for and end-for. Let's change the indenting here. We can do v-for and what we can do here is for item in cart.items. Let's see if we go into the issues doing that. As you can see, we've got one position, but this has, of course, broken all of these because with our previous code we created this item object inside our loop so that we could grab these attributes. We're going to have to refactor that as well. Going into here, I don't want the Shopify Liquid version of item title. What I'm going to do instead v-html like we did before, and do item.title. Using the information inside Vue for the image src, let's just look at our data object here, see the title coming across now. It's go into our items. Have a look. We've got this image here so we can just go get rid of that. Put a colon in because it's a dynamic value and we're inserting a variable here, item.image. Let's see if that worked correctly. Yeah. Then down here for item final line price, we'll format this value in just a second, but for now, let's just put in the explicit value. We'll do item, line, price. Double-check that that works. Yeah. Again, it's in sense and it's not formatted, but we'll fix that in just a second. Then here inside value, we want to fix that to the actual item quantity. As always, if we're putting in a variable here, dynamic value, we need to make that field dynamic by putting in that colon. I think that's it. There's no more Shopify Liquid inside here. Let's get rid of some of this white space and bring in this indenting. There we go. Actually here in the sidebar, we've got one. We're just going to remove that v-html and instead of going into a specific item, we're just going to do cart total price. Upsell we've still got a Shopify Liquid loop here, but we'll fix that in just a second. I'm going to refresh over here, and as you can see, all this information is now coming from our Vue instance rather than Shopify Liquid. Here it is still generated by Shopify Liquid but we'll get to that after we work on fixing this quantity, formatting the prices, and enabling a user to remove a product from the cart using this icon. First thing we're going to hook up this quantity selector so that it actually updates the quantity of the line item in the cart. For that, we're going to create a method, it's similar to how we did on the product page when the select is changed, we need to actually trigger a method on that event, and in this case, we're going to be submitting a Ajax request to the cart Ajax API to update the card on the back-end. Let's actually go after our created life-cycle hook here and create a method's object, and inside let's create this update cart method. Now the methods that I've created in this Vue instance, or I'm going to create in this Vue instance based of the different functions within the cart Ajax API. We've got the main ones is get the cart, add an item to the cart, update quantities, and then here is change. You can update quantities and properties as well. You've also got Clear, if you want to clear the cart, we're not going to have a button for clearing the whole cart, but we are going to trigger this API endpoint in order to enable removing a line item from the cart or reducing its quantity. Both are going to use the same end point and therefore the same method. Going back to the code, the two attributes that I'm going to be putting in here, variant id and quantity. That's based, of course of what we put through here. We've got a variant id and the quantity, and then we send that through in a update's object. What I'm going to do id and quantity, and then inside this method, I'm going to start the post request using jQuery and it's going to the cart update.js end point. Next we'll put in the body of the request, and for this, we can do it like this, or we can do it as a string. I think this is a little easier, so I'm going to do it this way. Essentially all we need to do is have updates in the square brackets, put the variant id, and then equals followed by the quantity. I'm just going to do that, updates, and by the way, these back ticks, if you're not familiar, allow us to put these variables within the string without having to exit and enter into the string over and over again. With the back ticks set, I'm going to put id with this syntax in there, and then after the equals put the quantity. Then we're going to chain a.then, just like we've done with get cart, take the response, JSON parse the response, and then we're going to be returned to the cart object to which we are going to set our own cart object equal to. Now, note that not every AJAX request will return the cart object. Some of them do return a single-line item, but in this case, the update.js endpoint does return the cart. Same thing, obviously, for get cart, with add to cart. I believe it just returns the item. We'll see that in just a sec when we build that into our upsells. But for now, we can rely that the update.js cart endpoint is going to give us back the cart. Obviously, this is not going to run, unless we attach it to our select. So let's go on to that change method. Just like we did with the product template, I'm going to put inside this @change, update cut. For the variant ID, we've got it set on the item. There we go, variant ID. Then the second argument, the quantity is also sets on the item, it's just quantity. Now, if we leave this like it is and then we go back here and we put this up, you'll see it goes back to one, and that's because we haven't set V-model here. So just like we did on the Products page, we need to change this to V-model to get that two-way data binding. Now let's refresh the page and let's see if this works. As you can see, when we put up the quantity, we see the line price and the total price update. Let's check all the individual parts are working. It should be if it's getting that result, but let me refresh our Network tab over here, clear the Network tab, click this, and you'll see that we are hitting that update.js endpoint. It is coming back green. There you can see what we're sending to it, and here you can see what we get back. It's a whole new cart object. That, as we have set in our view, is going to update this. If I go into my items and I scroll down to where it has quantity. Where is quantity? There it is. If I click on this, it'll take a second, but then once it comes back from the API, it'll update it on our view instance as well. Shopify by default output prices like this, in cents, with no formatting, so we're going to have to format it ourselves in JavaScript. But if you remember, we included this shopify-money.js file, and inside that shopify.money.js file, we have this format money method on the Shopify object. We're going to use that now. What I'm going to do is create a new method so we can use this at different points in our application. Here is the methods object. I'm going to carefully check where that ends, and then before it ends, I'm going to add in another method here called format prices. Inside this method, there's two prices I want to update. I want to update the line price and I want to update the total price. Those are the two ones we can see in our UI here. But I'm not actually going to update the values themselves, I'm going to create a new value called formatted line price and formatted total price. That way, we retain those old ones. You can do it any way you want, you can override them, but I'm going to leave the original in place and just create a new one. I'm going to create a loop here, looping through the different items in our cart. For each item, I'm going to create a new attribute, formatted line price, which is going to be equal to the item line price, but with a difference that we're running it through the Shopify format money method. I'm going to wrap that in format money. That's for the line item line price. Then for the cart overall, I'm going to create a new attribute, formatted total price, and that's going to be equal, again, to the result of Shopify format money (this.cart.total price). Now, of course, we're not running this anywhere, so let's run this after we set the cart. There's two places where we do that. I'm going to run it here after we update the cart, and run it here when we first load the cart. Now, refreshing the page, you'll see that we've still got the same values here. But if we go into our object here, you can see that we've got a formatted line price, and on the cart, we should have a formatted total price. What we have to do is go into our HTML and update those. So instead of line price, formatted line price, and instead of total price, formatted total price. Then it'll use the formatted version rather than the other version that comes directly from Shopify. There you go. Now we've got our quantity selectors working, and it is updating our line prices and total price. Now, you may have noticed here that we're not really writing DRY code when we've got this.cart equals cart and this.format prices two times duplicated. What I'm going to do is move both of these into a new method that I'm going to call set cart. It's going to take a cart object and then it's going to put that cart object into the cart and then format prices. Instead of this, what I'm going to do is put this set cart, and then we can do the same for update cart. That just makes our code a little bit more DRY so we can recycle code. There you go, it still works. Now that we've got that update cart function working and we're hitting the API successfully, it should be pretty easy to enable this button to work. All I'm going to do is create another method after format prices called remove from cart. All this is going to take is a variant ID. It doesn't need to know the quantity because the quantity is implied. The quantity, of course, is going to be zero if we're removing it from the cart. What I can do is just do this, update cart, put in the ID, and then for the second argument, the quantity, just put in zero. Now we're going to link this up on the onclick event for this button. I'm going to use that @ symbol syntax, and write here, "Remove from cart item.variant ID. Now let's check if that works. Perfect. We have just removed an item from our cart. If I go back to the T-shirt, let's add in a small red T-shirt. You'll see it's there. We can update the quantity, we can remove it from the cart, and it's all working. That is our main functions within the cart table. Let's take a short break, and let's actually code up these upsells here. We're going to add in another function here to add to cart, as well as to update and remove items that are already in the cart.
8. Cart Upsells and Animations: Of course, not every store is going to have upsells on their cart page. But it's a good opportunity to increase average order value by offering an easy way for customers to add additional products to their cart. Using Vue.js, we can make this interaction pretty smooth and seamless. If we go into our code editor again, you can see that we're bringing in these values through Shopify Liquid and we're doing our loops and conditionals through Shopify Liquid. Just like we've done up here, we're going to need to refactor this. The first thing we need to do is get these upsells inside an array on an actual view instance. After cart, what I'm going to do is create an array called upsells. Here we're going to load in that collection that we're including up here, but in our view data so we can make this section a bit more interactive and you'll see why we need to do that in just a second. Just like we did on the product page for variance, I'm going to create a Liquid loop here to bring in products from a particular collection. I'm going to use the shoes collection. Remember, this value here needs to match the handle of a collection in your store if you've imported the shoes and then created a collection with those shoes, and then verify that the handle is shoes then it should work. Otherwise, you'll need to do that. Obviously, we're going to have to put products on the end here. I'm going to close that, endfor, and then inside here, open up an object and let's copy some values over from Shopify Liquid. So tidal. Following this same format, I think I'm going to just breeze through these right now because we've seen this before and I'm just basically copying the data from the Shopify back-end using Shopify Liquid into our front end here. I'll fast forward through this a little bit. Make sure to end the objects with a comma here so that you have separation between those objects. As you can see here, we're bringing in the title, the handle, the image, the product ID; which will become clear why we're doing that in just a sec, and of course the variant ID. Because the variant ID, as we know, is what actually gets added to the cart. Let's refresh our page and just check that the data is coming across. As you can see here, we haven't actually got anything in our upsells array, let's check why that might be the case. I see here that we forgot an s on collections. Hit save on that. Now you can see the upsell array has all the same information as we have down here and now we can loop through this instead of using Shopify Liquid. Let's go in and refactor this code now. I'm going to inside here do a v-for or v-if rather. If upsells.length is greater than zero. We can get rid of this surrounding if statement now. That is some code that shouldn't be there. I'm just going to remove that. Then inside the loop, instead of doing this loop, what we're going to do is put it here. I'm going to do a template, v-for upsell in upsells. We put this around that to replace the for-loop. In order to mimic this limit, I'll show you in just a sec how we do that, we're going to have to add in the extra attribute here of index. This will track the index of the item and the array, and then we can do v-if index is less than five. Refreshing the page, you can see that we've lost our upsells generated by Shopify Liquid. That's because we actually need to go in here and update these attributes. Just like we did with the cart table above, I'm going to replace these with view attributes. Upsell title, and then for this one, the src, we're going to put the colon in front of it, remove this and use upsell image. I think that's it. Refreshing the page over here, see that still doesn't work. I think our issue here is that we need to move index to the element underneath. This is the difference between Vue 2 and Vue 3, you can do this in Vue 2 but not in Vue 3. Refresh over here. There you go. Now we can see the products are in there. We just need to make this index less than four and yet now we only have the four products in there. Now that we're using Vue for display of these upsells now rather than Shopify Liquid, we're on to the next step which is to enable these "Add to Cart" buttons to work. What I'm going to do, head over to the code and let's create a new method for add to cart. We've got our remove from cart here. I might just put it before the remove to cart. Add to cart. In order to add any product to the cart, we're going to need to know its variant ID. As always, we can refer to the reference here on the Shopify official documentation. I just found this by the way, by searching Shopify Ajax API. It should be on the Cart API just there. So not too hard to find with a bit of Googling. Here we go. We need to send through an items array with the variant ID of the product and the quantity of how much we want to add. Currently, our website, we don't have the option to add more than one quantity at the time of adding the product to the cart from the product page. But if we had that, we could use that value to put in as the option here but in our instance, we're just going to put in one for this value. All we need is the variant ID of the product, which we're going to add one of to the cart. We're going to open up a post request here. It's going to be cart add.js. Next, we're going to have the items array and inside we're going to have another object and we're just adding one item. ID is going to be equal to ID. This syntax means that we don't have to do this, we can just do that, and quantity is going to be one as we've mentioned just before. Then once we get a response back from the API, we're going to do what we always do JSON pause it so we can have it as a JavaScript object and then take the item that it returns. What are we going to do? Let's just console log it because we can't actually update our cart like we usually would because the add to cart Ajax request actually returns an item rather than a cart. Let's go back to here. Let's open up the Network tab, close down or clear the previous network activity. Let's add an item to the cart. Actually, before we do that, we have to actually link this. So I'm going to have to grab add to cart, put it on the click event here. I'm going to have to put upsell.variant ID. Let's refresh over here, clear the Network tab, and click "Add to Cart" and let's see what happens. If we go inside we'll see that we get an items array returned. It's not actually item, its items. Here you can see the data that's being passed in. I'll change this to items. But regardless, when we go into the console, you should see that we get that response back in our console because our console logged it here. If we refresh the page, you'll see that we've added that product to a cart. Like I mentioned though, we don't actually get the cart objects back. So how are we going to make sure that this cart updates after we add a product to the cart? In this situation, what we can do is simply request the cart again. We can do the same thing that we did in created. Grabbing this and setting the cart. But that would be very dry code because we will be repeating ourselves by having the exact same code in two different places. What I'm going to do is create a new method with this called get cart. This is going to be at the start of our list because it's central to our view instance here. Sorry I called it get start, get cart. Then instead of running it here, I'm just going to call this get cart. Exact same code, but now we can take this and run the same code on this method. The items come back, but we just run get cart. When we're running Ajax add to cart, it's actually running two Ajax requests. You'll see that now, unfortunately we have an error. I forgot to put a comma here after the method. Refreshing the page you can see here. What we're going to do is I'm going to remove this t-shirt, and let's add insane shoes to the cart. As you can see, there's a little bit of a glitch there with the image, but you can see insane shoes has now been added to the cart as well. What we did, added the product to the cart using the Ajax API and then ran another request to the API to fetch the whole cart. It's easier that way than trying to add in the item to the existing card object and then update the total. I find it's just easier to get the cart again after updating the cart or adding to it. Now we've hooked up all the main interactions. We're able to update the quantity of items in the cart. We can remove a product from the cart. We can add a new product from these upsells to the cart but there are a few little things here that we could do to make the user experience nicer and smoother. Number 1, if I add a product from the cart upsells to the cart, then I should remove that product from the cart upsells, or at the very least, indicate that that product has already been added to the cart. If I just click "Add to cart" on the same product again, all it's going to do is update that quantity, which may be okay, but I think could definitely be improved. The other thing is when you're waiting for that add to cart to happen, there's no feedback to the user. So suddenly you click on the button, nothing happens and then a few seconds later, once the asynchronous request has been finished, then it suddenly pops up in your cart. The other thing is, these don't have any nice transitions. It's all quick and a bit too snappy, and we want to smoothen that out a little bit. That's what we'll do in the final part of this video here. What I'm going to do first is create a filtered array of upsells. Like I mentioned, if a particular upsell is added to the cart, I want to remove that from the upsells array and not show it in this list of upsells. In order to do that, what I'm going to do is use a computed property. I'm going to create a computed property. I like to do this ahead of methods. We saw computed properties in the last lesson on the product template. We're going to use it again here to create a computed property called filtered upsells, which is just going to be a filtered version of the upsells. What I'm going to do, this is going to be a little bit of complicated code. But what we're trying to do is find the intersection of items in the cart and in the upsells array. If it's in the cart, remove it from this filtered upsell array. I'm going to break it down into two steps. First, I'm going to get the list of products in the cart. I'm going to call this products in a cart. But because we are going to be looking up via the variant ID, that's the way we're going to identify, we're going to use a map function in JavaScript. Instead of saying this.cart.items, which will give you the list of objects, I'm going to transform this into an array targeting the specific value of item product ID. I will show you what this does in just a second. I'm going to convert that into a string using the two string method. We can't do a console log, unfortunately inside a computed property. What I like to do it's just for debugging purposes, is move it out of the computed property into something like created. Then it'll run on page load. If we go now I'm refresh the page, unfortunately, it's coming back with an empty array. I think that's largely because we are still waiting on the cart to arrive from the Ajax API. Little bit of a hack, but I'm going to put a timeout here to just get it to execute a little later, like maybe two seconds later than what it was before. Let's just get this code to execute two seconds after created. As you can see, we've got an array with all the variant IDs. If we add another product to the cart, we've got gnarly shoes in there. Let's refresh. Two seconds later, we'll see an array with all our variant IDs. Moving that back into the computed property, what I'm going to do is take that array of variant IDs representing the products already in the cart, and then I'm going to filter the upsells based on that. Going to filter the upsells, take each individual upsell and if not, products in cart includes upsell products ID. This is a bit of a lengthy piece of code. But basically what we're saying here is this will return true if products in carts or array of variant IDs that relate to the products in the cart, includes the product ID of the upsell. Basically if the upsell is in the cart. If not, we want to return those upsells. It's basically filtering out any of the upsells that are already in the cart. What we can do now is instead of running through upsells, here we can put in filtered_upsells, giving it enough time to load and refreshing our page over here. We should start to see now that none of these products are already in the cart. If I add Old School Kicks, you'll see that the list of upsells down here updates. We've filtered out the upsells that have been added to the cart already. If I remove this, you'll see Insane Shoes goes back into the upsell array. We now sorted out that issue with the upsells. We don't want to upsell a product that's already been sold. We want to remove it if it's in the cart already. What I want to do, however, when I add a product to the cart is provide some feedback to the user. What we can do is use an if statement for that. What I'm going to put here in the Add to Cart button, I'm going to put in another button ahead of it. You'll see why I'm doing that in just a second. I'm going to give this the class of upsell-atc. Actually, save myself some time. I can just copy this. What I'm going to do also is add a modifier class on here, upsell-button--adding. What I'm about to do here is change the button state. Instead of add to cart, it's going to say adding, and that style should already be set up in the theme CSS file. Refreshing the page over here, you'll see that each of them have this adding state, but we want to alternate between them based on whether a product is being added or it's not. How we do that is let's add another attribute to our data. We can go into here and creates a loading state. When the page first loads, this is empty and then after a while, the asynchronous requests to the Ajax API will return the cart and update it. On page load, let's make loading true. But after every Ajax request where we've finished something, we're going to put loading to false. What are those situations? If we scroll down here, we could possibly put it here after get_cart. But as you can see, it hits the set_cart. What I'm going to do is go down to set_cart and then after format_prices, I'm going to put in this loading equals false. Of course, loading is going to be true when we first load the page, but then once it's set to false, we need the opportunity to set it to true again. For that, what we're going to do is when we're adding a product to the cart, we're going to set this loading equals true. When we're removing a product from the cart, we're going to set this.loading equals true. We're not going to do it for update cart because this is okay to me, we're not going to put in animation for this specifically. But for removing from the cart, I want there to be a loading state. It may not be clear exactly right now while we're doing this, but you'll soon see how that loading attribute will allow us to create a few nicer transitions. Let's have a look at if I remove this now, let's refresh over here. Start that again. If I click this, it will go loading for a split second, and then once it's finished loading, it will go false. Same thing with Add to Cart. Once I add it to cart, it will go loading true for a split second and then go to false. We're going to use that in just a second but before we do, I want to add one more loading state and that is on these particular items. We're going to have to do it on an individual item basis here. You can't do it on the overall cart object. What I'm going to do is move over to our upsells array here and put on a loading state on each of the upsell items. The way I'm going to do that, I'm going to call it adding, get real specific here, and call it adding_upsell. By default, that's going to be set to false because when we're loading the page, we aren't adding an upsell. But when we're doing this Add to Cart here, we want to set the adding upsell attribute on the particular upsell to true. What we're going to have to do for that to access the actual object is find the upsell first because right now, we only have the upsells variant ID. We're going to have to look up that actual upsell object. I'm going to create a const upsell here, and then find inside the upsells array the upsell with the variant ID that's passed in upsell.variant_id equals id. Again, now that we have the variant object, we can go here and do upsell.adding_upsell equals true. Then when it's finished loading, we can set the adding_upsell attribute on the particular upsell to false. Let's switch back to our front-end app now. Let's observe the upsells array as we add in a product to the cart. Sometimes this happens so quickly that it doesn't even have time to update. I'll click "Add to Cart" here and you'll see, it still says false. If I get a little bit faster, then you'll see it switches to true before it goes to false. There we go, true or false. You can see both the cart table was loading then and the adding upsell state went to true as well. What we can do now is add a transition to the cart table, and then also swap out these buttons depending on what state the upsell is in terms of whether it's adding or not adding to the cart currently. Let's go back in. All I have to do with these two elements, I've got them side-by-side, but now, I can just put v-ifs on them. I'm going to do v-if=product.adding_upsell, then we're going to display this. Otherwise, we're going to display the usual Add to Cart button. That should be all that's required for the interaction with the product upsells, looks like I've made an error here adding upsell. I've named this product instead of upsell, upsell.adding_upsell. Refresh. you can see that button does not show because we're not currently adding upsell. But if I click on this, you'll see that the buttons switches over briefly to give it a loading state before it adds to the cart, and then the item disappears because it's now been filtered out of our upsell array. I can do that with Gnarly Shoes as well. As you can see, it has an adding state before I add it to the cart. There we go. We've given upsells a bit of a loading state there, and now, we are going to wrap the cart table in a bit of a transition element. Right here, within the cart table, I'm going to add a transition element like we did on the product template. Put all this inside the transition elements called the transition elements name fade. Finally, one more step, what we're going to have to do is check for that loading state. We're going to cut all that but have it still on my clipboard. Create a div and put an attribute on their code v-show. If it's not loading, it's going to show. That might sound a little bit redundant, but it's going to give that functionality of fading in and fading out by not showing when it's loading. When it's not loading, it won't show and that will trigger the transition. Then when it comes back on, the transition will fade it in. Let's have a look at how that works. As you can see, when I refresh the page, the cart faded in. If I add a product to the cart, the cart fades out. If I remove an item from the cart, it fades out. The reason why it's doing this, the way we've got it to act like this is because we're setting this state of loading when we're adding a product to the cart down here and we're also updating that loading state when we're removing products from the cart as well. We're also on page load, having it set to loading until the set_cart is finished processing and then loading goes to false. That makes the loading state of our cart table a little nicer. I'm sure that you could improve upon this a lot more. I'm not necessarily a designer, but this is the core functionality and principles within view to make this happen. There you have it. If we close our Developer Tools, we've now got our product page working with some nice transitions. I can add a medium red T-Shirt to my cart. Go over here, upsell with some Insane Shoes. Go to Checkout if I want or increase the quantity. Maybe put this up to three. This is all synchronized with our cart Ajax API. If I was on an actual production store, I could hit "Checkout" here and check out. Actually, I think it might actually let me check out. There you go. Here you go, that's my selection. Yeah, that's pretty much it for the cart page. I know this is quite a long video or set of videos to get this cart page working. But this is the biggest area of interaction within a Shopify site most usually. Think about when you go shopping, things stay on the shelves until you pick them up and put them in your cart or put them back. The user's main interaction with your site that makes sense to use Vue with at least is the cart interactions. Obviously, also, the variant switching very handy to have Vue for that. As I mentioned earlier, if you did want to filter your collection page, you could have some hectic filters in here. If you had a lot of products within your store and a lot of different attributes to filter on, you might make this a Vue app as well. But the main principle is anytime you've got some interactions that need to happen live on the front end, you will at least want to use JavaScript and I would encourage you to use Vue or some other kind of framework. Obviously, you've learned Vue in this class so that's what I would recommend but this should be a good starting point for you to start incorporating Vue into your projects. Depending on when you're watching this class, the next video might be a bonus video on using Vuex in case you are sharing data across different parts. We've used the cart and the cart page here, but maybe you want to update the cart on a different page and synchronize that data. We'll be learning about the Vuex store in next video, depending on when you're listening to this. Otherwise, I will see you in the conclusion, and I look forward to getting you started on your own class project.
9. Bonus: Storing Cart Data with VueX: Welcome to this bonus video on Vuex. I wanted to cover this as a bonus in this class as it's a concept in Vue that I've previously utilized using Vue in Shopify theme development and something that might be valuable for you to learn as well. In this class so far, we've created two apps in our Shopify theme, one for the product page and one for the cart page. Currently, these are very separate. They each carry their own data, properties, and methods, and these are exclusive to each instance. To share data between multiple instances and components in our Shopify theme, we can utilize a library called Vuex. If I look up Vuex online, the first link is what is Vuex. As it states here, Vuex is a state management pattern and library for Vue.js applications. It serves as a centralized store for all of the components in an application. We've rules ensuring that the state can only be mutated in a predictable fashion. Essentially, Vuex allows us to create a centralized store of data that all Vue Instances and components can access. Now, why would we need this specifically in Shopify theme development? Well, there's one clear example and that is if we were to allow for front-end cart interactions outside of this cart page. For instance, one feature you might have noticed on a Shopify site before is a side cart. Like on this store, for instance, if we add a product to the cart here, you'll see that we have a little interaction here. We have some cart information here, but we can also go to the actual cart page and see that same information. If I was to add one of these upsells to the cart, that is reflected in this side cart as well. That means that data is shared between the side cart and the cart page. It's basically coming from the same place, it's coming from the cart object. All of this needs to be in sync. So how we can do this is via a Vuex store. Now note whenever I refer to a Vuex store, I'm referring to a centralized store of data, not the Shopify store itself. Sometimes it can get a little confusing talking about Vuex stores on Shopify stores. Therefore, I'll make sure to refer to the Vuex store as the Vuex store or Vuex for short. All right, so in this video, we're going to move the cart data and methods into a new Vuex store. Update our cart page, and then I'll show you how we can use this new source of truth to update other areas of our theme. Sound good? Let's get started. All right, so first thing we're going to do is include the script for Vuex. All right, so I'm going to go back to this documentation at vuex.vuejs.org, click on installation, and what we're going to have to do is make sure that the version that we're using is compatible with the version that we're using of Vue. So in this class, we used Vue 3. So if you are following along the same as this class and using Vue 3, then you're going to have to use version four of Vuex. So click on V4 right here, switch over to that documentation, go to installation, and I'm going to grab this CDN link of Vuex 4. Copy that, head into our code. In our theme.liquid after our Vue script tag, I'm going to add in Vuex. So just making sure that we're using Vuex version 4 if we're using Vue version 3. So now that we've included the library for Vuex, we can now create our Vuex store. What I like to do is put it inside a snippet. As you can see, this theme has no snippets right now. So I'm going to actually create the snippets folder. Inside the snippets folder, I'm going to create a file called store.js.liquid. We're not actually going to use liquid variables in here. So an alternative could be to put it in theme.js, or create a js file and assets and include it. But this allows us to include it on specific pages using the include keyword, which I'll show you in just a second. So what we're going to do is let's do that straight away. We're going to use it on the cart page. So I'm going to make some room up here and let's include that snippet. Include store.js. We don't need the.liquid part because that's implied when we use include statements. All right. So two parts to getting this all hooked up. One, we're going to have to create a store here. Then we're going to have to link up that store to the instances that are going to use it. So I'm just going to use the standard convention here. Let's not forget to open up our script tags because we are in a.liquid file. The standard syntax and standard naming convention is to just call our store, store. We can then use the create store method of the Vuex object like this. Inside here we will create our Vuex store. All of our options for the store will go right in here. Before I do that, I just want to link it up in our Vue instance here. What I'm going to do is change this to a more appropriate name because it is the options object. I'm going to rename this to cart page options, cart page options, and then I'm going to store this in a variable. So we're going to call this cart page. So this will store the actual app now rather than the options. Then what I'm going to do is mount the cart page and add another step here for using our new Vuex store. So what we do is do use and then we put in the variable name of our Vuex store, which we just called store. Ideally, we should only have one store for the whole website. So it makes sense to call it store and then we can just include it like this. I'm going to go back to the store here and just in my code editor, change the language detection to HTML. That'll get rid of those squiggly lines. Actually, there's one more step and that's to go into our options here and just make sure we add in store to the options. Okay, this is the equivalent of store: store. So we can just save some space and just put it in as store. Then that now enables us to reference the store within this instance. Okay, so what we're going to do is we're going to move our cart data into our newly created store. So I'm going to cut this. I'm going to leave the upsells in there. Inside our store, I'm going to create a state function. Then just like we do when we're returning data inside a Vue instance, I'm going to copy and paste this into our state here. I think we can bring back some indenting here. Yeah. All right. Now, a little bit of Vuex theory. We have some core concepts here. State, which is basically data getters, which allow us to access that data that we've just set on the store state. Mutations, which are the only way to change data in a Vuex store. So mutations mean anytime we change data. Actions which we dispatch to commit mutations, and then modules which we're not using in this lesson. But essentially what we're going to do is we're going to use getters to get the data. Then we're going to use actions and mutations to set the data. Okay? Now, if you want to get in-depth about why we use mutations and actions in order to change data and not just one or the other, you can read about it here on the documentation. I'm not going to go into the theory of actions and mutations in this particular video, but all the information is there if you want to look into it. All right, so let's go back to our store right here, our Vuex store. Let's think about what mutations we might need to do. Basically, the only mutation we need to do on here is to update the cart. Remember every time we are changing the cart, we're just getting a fresh cart object from the Ajax API and setting that new data. Okay? So we only need one mutation. You can choose whether to do this or not. But I like to use the capital's convention when naming mutations. So I'm going to open up a mutations object. In here, I'm going to create a set cart mutation. The first argument is going to be state. This is standard when running mutations. Then the next thing is going to be the payload, which is going to be the cart. So that's what we're going to pass in. Then all we're going to do is do state cart equals cart. Again, this might look a little bit redundant, but there's some method to the madness with Vuex. It's following a specific pattern. Again, if you want to read more about this, you can do so on the documentation. But after we've created that mutation, what I'm going to do is create some actions. This is where we start to transfer some of these asynchronous functions over from our cart page into our Vuex store. But before that, I want to create some working code and show you this is actually working. So what I'm going to do is I'm going to close down this and open this up in a split tab. Let's get rid of all the other tabs here. What we're going to do is refactor this, move, some of this code over to here. Now, the first thing that happens when we load the page, of course, is we get the cart. So let's recreate that using this Vuex store. Okay? So what I'm going to do is before actions actually, I'm going to open up a getters object here. Here we can put our getters. Again, this is going to look a little redundant. Why can't we just access the state directly? Again, there's theory behind it that you can look up on the documentation. But how this works is in order to get to the cart object, we need to write a function just like this, and it's just going to return state.cart. So in order to get that data from our Vuex store, remember we already included the store up here, we just need to create a new computed property which just pulls that data from the Vuex store. So all I'm going to do is replace that cart object that we just removed with the computed property. Here we go, I'm just going to call it cart and this is where we bring in our Vuex store data, I'm going to first reference store.state.cart. Now of course, when you first load the page, we're going to have this data. So that's why on created, we run get_cart, but now we're going to be doing it through Vuex. What we're going to do is put this into an action. So down here, I'm going to create an action called get_cart, and from the first argument, I'm going to pull off the commit method. This is standard practice with actions, and we don't have a payload here, so that's all I need and then what I'm going to do is mimic the get_cart action here. I'm going to grab all of this, bring it in, and instead of running this set cart method, I'm actually going to run the set cart mutation and the term for that is called committing. What we do here is instead of this we do store.commit, then we put the name of the mutation set_cart, and then as the second argument, we put the payload in there, which is what we get returned from the API. Then that's going to set the value of the cart to what we get from the API. Again, this is just pretty much exactly what we have over here. We're just putting it into Vuex. So on created instead of this.get_cart, we're going to instead dispatch the action we have here. So what I'm going to do is to store.dispatch get_cart. Now, if we run this code now, let's go back to our shopping cart, open up our DevTools, refresh the page; you'll see that when we go and look in here, our cart table is there, but it's currently set to display none. You can see your cart is empty, but it's not displaying as you can see here. The reason why is because we've now lost that part of the code here where we set cart and changed the loading to false. So what we're going to have to do is get this action to return a promise and then when that promise resolves, then we're going to set loading back to false. So what we need to do for this is here, where the get requests start, we need to just put return in front of it. Then now we can put a dot then over here. So when the get_cart action has finished dispatching, finishes running, then we're going to set loading to false. All right, now let's refresh the page, and you can see now we've got that value coming up. Now if we go to shop the T and we add, let's say a small black t-shirt to the cart, you'll see it shows up not from our cart data that's stored on the view instance, but from a computed property of cart that is pulling from our store. So we've got the beginnings of our refactor here, and we can see that that's now pulling from our Vuex store. Let's go back to the code and continue working on this. So moving down in our filtered upsells, I can see this.cart. We're now using cart as a computed property, pulling this value, so I'm going to replace this.cart with store.state.cart. That's the first change, and then as we go down, we don't need get_cart anymore because that's now a Vuex action. We are just going to check where we refer to that. As you can see, we're using it in add to cart as well. Let's just remove that and then we'll refactor add to cart when we get to it. In set_cart, we're doing that through the mutation now, so I can remove that, but what I'm going to have to do is there's this extra step in here for format prices. So let's move this logic out of here, and let's put it into the actual mutation. I'm going to remove format prices and inside this mutation here, let's take the cart object that is submitted as the payload, and let's update that before we put it into our state. For that, all we have to do is just remove this dot because we're now using the cart that's passed in, in this payload. So this is going to create those new formatted line price, and formatted total price, and then insert it into state.cart. All right, so back to the actions here. Let's go further down. We can get rid of set_cart now, and we're going to have to create a new method or a new action for this one right here, the update cart. For this one, I'm going to assume that the only time we're updating cart we're just doing it for one line item so we're going to keep this syntax. So what I'm going to do is grab this asynchronous code here and let's create a update cart method here and of course, the first argument is going to be commit, and then the second argument is going to be an object with those same two arguments; ID and quantity. Then inside we can do that post request and so that we can make it return a promise, we put return in front of the Ajax request. That of course allows us to chain a dot then on the dispatch like we've done over here. Let's get rid of this code and of course, this.set_cart needs to be replaced with store.commit set_cart, and then in our update card method here, all we have to do is trigger this action or to use the Vuex terminology, dispatch the action. So we do that from the store object, dispatch, update cart, and then an object with the two parameters, ID and quantity, and that's all we need to do. Because in our update cart method we're just using that to update quantity as you can see over here, we don't need to update loading states therefore, we don't need to chain a dot then on the end. But for add_to_cart, we are going to definitely have to use promises. In fact, we're going to have to chain some promises together. I'll show you how we do that right now. So after update_cart, we're going to put in add_to_cart, and let's use the same syntax as before. The first argument we're going to pull off the commit method and then the payload is going to be ID. Let's pull off this ajax request into here as I did before so that we can deal with the promise as it comes back from the action, put a return in front of it, and unfortunately the indenting is all wack here, so let me just fix this quickly. All right, that'll do. Now to refactor this code, we have to replace this method with another dispatch. Then this we're going to deal with once the promise comes back from Vuex into our instance. Let's just remove that and we'll do that inside the instance a little later. What I'm going to have to do to dispatch an action from within another action is just to pull off the dispatch method right here on this object, and so instead of this.get_cart, I'm going to run dispatch get_cart, and then that's all we need to do. Because we're going to handle the promise inside our instance, we don't need to do anything else after that in the Vuex store. So let's do that right now. Let's go into here, into add_to_cart, and let's do our dispatch, store, dispatch, add_to_cart with our payload of ID, and then we can handle the promise like this and first update the loading state to false, and then upsell, adding upsell, false. It makes sense to update these loading states because this relates to the UI that we're dealing with on the page itself, so inside this particular instance, but everything to do with the cart data should be centralized within this Vuex store. Hopefully, that explains why we're handling some promises within Vuex and some promises within the Vuex instance. All these loading attributes had to do with the UI on the cart page itself, not the cart data. Finally, we've got this remove from cart method, which is triggering this.update cart. What I'm going to do here is replace this update cart method with the update cart action on our Vuex store. As we've seen before, store_dispatch, same payload as before. We just need to put it in an object form, ID and zero, then we're going to set this dot loading to false after it comes back. Let's see if that'll work. I'm not sure whether the explicit value here is going to error. Yeah. As you can see, it doesn't like the zero there. What I'm going to do is just do const quantity equals zero, and then replace that with quantity. Hit "Save" on that. Refresh the page over here. As you can see, we've got an error, "Cannot read property total_price of undefined". If I scroll up here, you can see I've got a reference to this here. Just need to remove that, because we don't have access to this. This of course, was referring to the Vue instance, the Vue app, but now we're inside our cart, so we just need to look at what we've passed in, and the store object. I'm going to fix that up, refresh over here, and now we can see our black shirt coming through. If I am to update the quantity here, you can see despite the errors in our JavaScript console, this is now working. If I hit the trash can here, it'll remove that product from the cart, refreshing to show that this is legit. If I hit "Add to cart" on one of these, it now works. Now, all of this data is coming from our Vuex store. If I go into here, unfortunately, with this latest version of Vue DevTools, I don't think we can actually look at the store itself, there's is not a Vuex tab like there was in the previous versions of you Vue DevTools but what we can see is that we're getting the cart objects from computed rather than data here, and that makes it clear to us that we're pulling that cart object from somewhere else other than the instance. If I remove that, you'll see that that cart object is updating in accordance with what is in the Vuex store. Now, of course, what you see here is exactly the same as what we had before. It might not be clear to you why this is any different or any better, so I thought I might show you how we could use this new centralized point of truth to interact with the cart on any other page on our Shopify website. What I'm going to do is actually put the item count in our cart up here, and have this live update when we add a new product to the cart, currently we can only do it from the product page here. Of course, we can do it on the cart page, but we're already on the cart page, so it doesn't really make a lot of sense to do so but perhaps we want to add to the cart and there's to be feedback straight away without reloading the page. Now, we can use that cart data to update something in our header to indicate that we have added a product to the cart without having to go to the cart page. I'll show you a little example of that right now. What I'm going to do is create a Vue instance for the header, and unfortunately, we don't have a section file for the header. We could go and create that right now, it wouldn't be too hard but let's just keep the code in our theme.liquid. I'm going to close down this cart page here. What I'm going to do is include the same snippet over here, just above our header. We're going to do include store.js, and so now we're including that Vuex store in our theme layout file as well. I'm also going to add an ID of header to this element here so that we can clearly identify it in our Vue instance. Then like we've done throughout this class, I'm going to create a Vue instance underneath this HTML code here. First, we're going to create a options objects, header options. Let's just open up an empty object for now, and then I'm going to create another variable that's going to hold our app header, Vue.createApp, insert those header options, and then I'm going to mount that using the ID that we just added to the header, and then I'm going to get that Vue instance to use the Vuex store. Now, if I refresh the page over here, we should see under here that we've got two apps. You'll see that the information that we had here has now gone, because it's sitting under App 1 over here. Let me just see if I can zoom out and show you. Here we go. As you can see, App 1 and App 2, we haven't actually named them, so it's a bit confusing. Let's go in and actually do that right now. I'm going to give this instance a name of "Header", and let's go in and name our other instances in the cart page. I'm going to put in a name into the "Options", "Cart", and then "Product Page", "Name", "Products". That's just going to give it a name in the Vue DevTools so we can easily identify it. Let's go back, refresh, look at the DevTools, and you can see we can switch between the Header app and the Product app here. We're running two apps on the one page. Of course, if we go to the cart page, you'll see that we have Header and Cart on there as well. Let's go back to the product page. We can now switch between the different apps. Let's add that cart data to the header. So what I'm going to do is bring in that store, and then like we've done for the Cart page, I'm going to create a computed property that pulls the cart from the Vuex store. This is going to be cart, exactly like we've done in the Cart page, store.state.cart. Then in order to populate that cart on page load, what I'm going to do is tap into the lifecycle hook we've created, and then in this lifecycle hook, dispatch the get_cart action. Let's refresh the page now with our DevTools open. It looks like we've got an error, "Unexpected identifier, store". That's because I forgot the comma here. Let's "Save", "Refresh". Go back to our Vue DevTools. Yeah, we've got header and product. If we click into the header, we can see we've got our cart object coming from the Vuex store now. How we can use this is we can now go into here, let's create a span, and inside here in the v-html, we can add in the item count from our cart. I'm going to do cart.item_count. This is, of course an attribute on the cart object, you can see here. If I "Save", "Refresh" the page over here, you can see that we've got one item in the cart. Of course, we could have achieved the same result using shopify.liquid but what we're going to do now is update the product page to submit the variant ID through Vue rather than through the standard Shopify means which ends up processing the "Add to cart" and moving you to the cart page. As you can see here, we've already got that interaction happening between the header and the cart page. Just to complete this, let's go back to the product page. Let's go to the product page code, right here. What we're going to do is right here, add in an event handler, we could actually remove this form completely and just have a button that does this as well, but let's just stick with the form for now and make it easier. We're going to attach this to the submit event, and as an extra modifier, we can put in here called prevent, and that's going to prevent the default submit functionality, which is, of course, what we want. In here, we can put a method that's going to handle the Add to Cart. I'm just going to create that now. Let's go down after the final method and create a handle_atc function. All it's going to do is take the ID of the variant that needs to be added to the cart, and we're just going to dispatch the "Add to cart" action with the payload of that ID. Then we're going to go up here. Where was it? Right here. To handle_atc, and as the argument in that method, we're going to put the current variant ID. This makes the hidden fields a little bit redundant, but it should still work. Let's go over here, refresh the page, and let's see what happens when we click "Add to cart". You can now see that the page doesn't refresh, but something did change. The number in the cart updated. We could add in other interactions like we could have it pop up here saying, "Recently added", we could still redirect to the cart, we could have a side cart pop out like we saw earlier. We could do all sorts of things but the point is, we have now got synchronization of data between our header and our cart page. As you can see, the number in our header is updating when we update the items on our cart page, even though they're in separate apps, and the reason why is we're sharing data between them using Vuex. That's a little bonus on Vuex for you guys. If you are building a fully featured side cart while having a fully featured cart page, then you're going to want to share data between them, and Vuex is the go-to solution for doing this work in Vue. I hope you learned something from this lesson. If you want to incorporate a side cart and use Vuex in your class project, I encourage you to do so. I'll give you more details about your class project in the final video that's coming up next. Thanks for watching. I'll see you in that one.
10. Bonus: Adapting to JSON Templates: In this short bonus lesson, I'm going to show you how to move our Vue apps into section files as opposed to leaving them in liquid template files. The reason I'm showing you this is because of Online Store 2.0. If you're not familiar with Online Store 2.0, I encourage you to check out my first class here on Skillshare, Shopify theme development: How to build and customize your own online store. I've added three additional videos in there to account for the Online Store 2.0 changes. Essentially, what has changed is that template files can now be either JSON or Liquid. To adapt for this new way of making themes, I want to show you how we can move our apps intersection files, and then transform those templates into JSON files instead. I'm going to dig into the theme here, I'll just show you what I mean. Here under Templates, you've got all the template files coded as Liquid that, of course, allows us to insert liquid code HTML, CSS JavaScript, all in this template file, and that's what's used as our template. But if you click on this nowadays, you will see that there is an option for a JSON template. What I'm going to do is I've duplicated the theme to keep that one untouched, and in here, I'm going to click "Edit code" on the duplicate, and let's start to refactor our project here. First things first, we want to create a new section to house. Let's do product template first, so I'm going to create a new section, and I'm just going to call it Product Template. Don't have to put the.liquid in there because that will be automatic, and now we're just going to have to populate the section name, just going to call it Product Template or Main Product, something like that because we may want to put some other stuff on it. Main Product Template. Now I'll just hit "Save" on that, and I'll go over to our product.liquid template. This is where we have our Vue app for the product page. I'm going to copy all of the code using Command A on a Mac. I'm not sure what it is on a Windows, then Command C. Then I'm going to go over here, create some space at the top, and paste in our code there. Now that we have our code pasted in and saved, we can delete this template file, and this will give us the space to create a JSON template. I'm going to click "Add new template", and go down to product, and here you can see we can create a product.JSON template now. Going to hit "Create template" and now we've got the basis of our product template. All right. I'll go more in depth into this in Lesson 11 of Shopify theme development: How to build and customize your own online store. I'm going to breeze through this a little bit, but essentially what we need to do here is add the required fields in order to use this. It doesn't allow me to save unless I make a change, but you'll see that if you don't include this, it will error. We need to include these compulsory fields. First of all, we need to give this product template a name, and I'm just going to give it the simple name of Product Template. Now inside our section's object, we need to link up that product template that we just created. I'm going to start off by calling it Main-Product, and let's open up an object as the value of that. Then all we need is to put in a type, and the type needs to be equal to the section name; the section filename minus the.liquid. That's just going to be product-template. All right. In our order array, we need to put in the name that matches this, so main-product. This seems redundant at this point because we only have one section, but if we had multiple, we can define a different order for them here in this order array. I'm going to hit "Save" on that. As you can see, no errors. Then I'm going to hit "Customize theme". Now that we're inside our store editor, we need to click up here to change to the product template that we just created. It's just going to be default products because it doesn't have a dot after product, it's just product.JSON. If it was product dot something else, then it would come up as an alternate like this one. I'm just going to click on that, and here you can see we've got our product template still. If I go to Shop the Tee, you can see we've got our app, and it's still working. That's because we've included our section file here, it doesn't have any editable sections, but it has been linked. Now this isn't the most exciting lesson because we haven't added any new functionality whatsoever, except for the fact that we can now add other sections and reorder them. Because this is a bare-bones project with no other sections, we don't actually have the ability to add another section here because there are literally no other sections. But using the Online Store 2.0 system, if we were using a modern theme, we could actually now move this around, add new sections if we wanted to. That has been essentially what this video has been about, adapting to Online Store 2.0. You might be using a newer theme that has all of its templates as JSON. In that case, we need to adapt our practices just a little bit to adapt for this new change. All we've done here is just moved the Liquid code into a section file, and included that section file inside our JSON product template. We can still do it the old way, we can still throw in or keep this code in a product.liquid template, but now we're moving in this direction with Online Store 2.0. I just wanted to throw in this video here for those of you who are working with modern themes, or have opened up a modern theme, and wondered what the hell. Hopefully this video has provided some context for you. Any questions, as always, feel free to leave them in the discussion box below, and I'll see you in the final video for the conclusion.
11. Conclusion & Class Project: This concludes this class on using Vue.js in Shopify themes. For your class project, I encourage you to integrate Vue.js into whatever Shopify theme you're currently working on. Keep in mind that most production level themes already use a fair amount of JavaScript, so you'll likely have to refactor some of that JavaScript in order to make room for Vue.js. As I've mentioned throughout this class, this topic is quite a specific one within Shopify theme development, so if you're unsure about anything regarding the foundational topics of HTML, CSS, JavaScript, Shopify liquid, you can find plenty of lessons on all of that here on my Skillshare channel. All you got to do is click on my name and then check out my other web development classes. As always, if you would like to learn more about Shopify theme programming and theme development, follow me online and check out my YouTube channel for additional tutorials. Finally, if you need any tips or guidance on what we've covered in today's class, be sure to leave a comment in the discussion box below, and I'll do my best to point you in the right direction. Thanks as always for watching and I hope to see you again on some my other classes.