![Astro for Fast Website Development](https://static.frontendmasters.com/assets/courses/2023-08-24-astro/thumb.webp)
Check out a free preview of the full Astro for Fast Website Development course
The "Implementing the Cart" Lesson is part of the full, Astro for Fast Website Development course featured in this preview video. Here's what you'd learn in this lesson:
Jason walks through implementing some shopping cart functionality. Displaying cart items, subtotal, total, and implementing the ability to remove items from the cart are covered in this segment.
Transcript from the "Implementing the Cart" Lesson
[00:00:00]
>> Then we're gonna do a few things here. So I want to set up an EmptyState for the cart. That is going to be a kind of a functional component. If you've seen one of those, this is gonna be very similar to what we would write in React.
[00:00:15]
The biggest difference is that Solid also doesn't care about class. You can just use class, you don't have to write className. And the difference between the way that Astro does scoped styles and the way that CSS modules do it is that you have to import the stylesheet as a JavaScript object, basically.
[00:00:38]
And then the classes come off of that object. So we've got a class in here in the module called icon. So we're just getting styles.icon to apply the icon class. A little funky if you've never used it before. Once you get the hang of it, I find it to be the friendliest flavor of CSS and JS.
[00:01:02]
So then we're gonna set up a span with a roll of img, and then we'll give it an aria-label of a hot dog. And then I have to remember what my shortcut is for, there it is. Cmd+Ctrl+Space will open up your emoji thing if you're on a Mac.
[00:01:24]
And then we're going to set up a class of styles.empty. And inside, we'll just say your cart is empty! Add a sandwich kit or two and give flavor a chance. Okay, so that's gonna be our EmptyState Next, we're not actually gonna implement the Checkout. So instead, we're just gonna have a little like, hey, this isn't here yet.
[00:02:01]
So we don't want it to be broken, we don't want it to do nothing. But we also don't wanna spend any time building it because it's not really related to Astro. We would work the same no matter what framework you're building in. So we're gonna add this class, and we're just gonna say, Checkout is not implemented yet.
[00:02:24]
Okay, so that's ready to go, and now we can get to our actual cart, which is gonna be the bulk of what we're building. So we're gonna export a const Cart, and that is also this functional component export. And let's set up the pieces we need here. So we're gonna get into some signals.
[00:02:46]
We're gonna set up one called showNotice and setShowNotice. So very similar to the way that you use useState. So we're gonna create that signal, and by default, the notice is false. Then we're gonna get the subtotal, and that is going to be useStore. Now, useStore is from nanostores.
[00:03:08]
This is the Solid-flavored version. And it just takes whatever the value of the nanostore is and converts it into a signal, or in React, a useState value, or whatever it is that you need so that you can access it inside of your component library. So we're gonna useStore, and with this one, I wanna get the subtotal.
[00:03:32]
And then I'm gonna just duplicate that straight up, and we want cart. And I forgot to put the dollar sign on this subtotal, but bear with me, I guess. No, I did the thing. Okay, here's what we're gonna do. That was maybe why I didn't have that dollar sign on there, was so that I would be able to do it here.
[00:03:58]
So what I just did is I aliased this to cart without the dollar sign so that I don't have a variable name collision lower in the code. So now we've got access to our subtotal, we've got access to our cart, and we've got autocomplete here. You can see that this does know that it is a record of CartItems.
[00:04:18]
Then we can start writing some actual component code. Let's get some code returned here. So I'm gonna set up this as an aside. That's a built-in HTML component. I'm gonna give it styles of cart. And then inside, we just wanna kinda give an indication of where you are, this is Your Cart.
[00:04:41]
Next up, we're going to use the Show component from Solid. This is very similar to using a ternary, but the nice thing about it is that it's a little more intentional. You're saying, I'm gonna give you a condition of when if I, my goodness, I can't type. Okay, so give a when.
[00:05:00]
And then I'm gonna say, Object.values of cart. And you see I'm calling this like a function. This is the thing that's unique to signals as compared to React state. In Solid, you have to call the state or the signal like a function to get the value. So that is why we're doing that.
[00:05:22]
And then I'm gonna check that the length is greater than 0. And that's our condition for when we would show this thing. If that condition is not true, we can provide a fallback. And the fallback I wanna provide is that EmptyState we defined. Okay, so that's how that piece works.
[00:05:40]
And then inside of it, we can do, now we know we have CartItems, so we can just display them. We don't have to do any defensive coding here. The Show component was our defensive coding against having an empty list. So we have a class here that we're gonna set to styles.items, and then we're gonna loop over those values again.
[00:06:02]
So again, we are storing these as an object. So to get them in a list that we can iterate over, we just run this Object.values. So we get the cart, and then we're going to map. And for each thing here, we're gonna get an entry, and that entry is a CartItem.
[00:06:19]
So now we'll have some autocomplete here. And we're gonna check, if there's not an entry, we will just return null item. And otherwise, we're going to return, A big old pile of markup. So it's gonna be a class, styles.item. And then inside of that, we're gonna set up a span class of styles.quantity, and that contains our entry.quantity.
[00:06:56]
And then we can start duplicating this a little bit because we're just gonna list out the quantity, the name of the thing, the price of the thing. So in here, we're gonna call this one styles.name, and this contains item.title. Then we're gonna add a button for the remove.
[00:07:18]
So we're gonna call this remove, styles.remove. I don't know why it didn't make this a button, but I don't have time to rewrite the style. So just know, for everybody out there, it should have been a button to be more accessible. And I'm very sorry that I didn't.
[00:07:33]
I know what I did, I put the button inside. [LAUGH] So it is a button, it is a button, I'm just not reading ahead of myself here. And so we'll say, remove item, and this is gonna be our button. And onClick, we get to pass in this function that we wrote, removeItemFromCart, here.
[00:07:57]
And we're gonna pass in the entry.item.id, right? So then inside of this, I like to use the multiplication symbol as an x because it's kinda more centered than the regular x character. There might be reasons not to do that, I am not an accessibility expert, but that is how I ended up there.
[00:08:28]
Ended up with an extra parenthesis there. Okay, so that is now valid. So we've got the title, and then we've got an onClick that just removes that particular item from the cart there. Then I'm going to take this outside. Let's get the price. So outside of that span, I want the price, and we can change this to item.price.
[00:08:59]
And we've got our closing ally, all good, then we've got this closing ul, all right. So the last thing then is we wanna show the subtotal. So we're gonna set this up as styles.details because there's a few pieces in here. We've got a paragraph that we'll call styles.subtotal.
[00:09:25]
And that is going to contain a span. And these are all kinda presentational things. So I tried to keep this as simple as I could and also make it look nice. But again, I love CSS, so I just end up playing with stuff. So then I'll give it a hard-coded space because I'm gonna break this, but I don't want it to be crammed into the label.
[00:09:51]
And I'm gonna use that formatCurrency, pass in the subtotal this time. And again, that's a signal, so we have to call it like a function to get that to show up. Then outside of this, I'm gonna copy this one and just drop it right down here again. So this one is going to be called shipping.
[00:10:15]
And it keeps that style of label, but we're gonna change this label to shipping. And just for a little bit of like, hey, let's make this feel like a real app, we're gonna delete the shipping price and insert FREE. Hey, free shipping. Finally, we're gonna grab this one one more time, drop it down here.
[00:10:39]
And this is going to be our total. And that will be the actual total. So now we're gonna add the total. So we're gonna use this formatCurrency again and just recalculate the subtotal. But if we add shipping fees, if we add taxes calculated, we would need to probably store another computed store to factor those pieces in, and then we would be formatting the total itself.
[00:11:09]
But because we know that nothing else will get added to the amount, we can use the subtotal again. And our very last piece here, no, it's not, second to last piece here is the Checkout button. Like I said, isn't actually gonna work, but we are going to put it there so that it doesn't, we want it to feel as complete as we can.
[00:11:34]
So here's a button, gonna give it a class, and we've got this global class of big-link, which is what we used in the hero. So we wanna share that class there. And then this one is going to include an onClick handler that will use the setShowNotice to true.
[00:11:57]
And then we can say, Check Out. I spelled button wrong, so I'll fix my spelling here, Okay? That was [INAUDIBLE]. I love autocomplete until it gets too helpful, and then I get really confused as to what's going on. So then the last thing we're gonna do is use the Show component one more time.
[00:12:22]
And we're going to use that to say, when the showNotice is true, we want to show this CheckoutNotice that we defined up above. So that's that component that just says, Checkout's not implemented yet. And our goal here is, again, if somebody clicks that Check Out button, we want them to know that we're aware that the thing is broken, we were not shipping a dead button.
[00:12:47]
And then they're like, is the site broken or is this not implemented yet? We want them to be very clear, this is not implemented yet. Okay, so, This is probably one of the most complex pieces that we've done so far. Does anybody have questions about how that works before I move on here?
[00:13:11]
>> I have a question more relevant to shop-items.astro.
>> Sure, yeah, let me pull that up.
>> So in here we have an Astro file or just making a fetch call.
>> Yes.
>> If something goes wrong, is there an Astro way of handling that? My treat is more like a vanilla HTML, R, and JS situation.
[00:13:31]
>> All right, let's find out, just start the site. So it's not handling it for you, right? It is telling you that something went wrong, and it'll tell you, I think, exactly where it went wrong. I guess it doesn't. [INAUDIBLE]
>> It should console log it, yes. Or actually, that might be in this output.
[00:14:08]
Yes, so here's our actual output here, and it's gonna give us the Not Found. So assume you're gonna need to kinda do your own handling there, this is the laziest error check. At least if it breaks, I know I can go look at the console, [LAUGH] but yeah, you would wanna control that on your own.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops