Okay, now let's take a look at updating the nav bar with the number of items in our shopping cart or
basket.
And let's head back to VS code to do that.
And we will open up our nav bar.
And let's close the others down for the time being to focus on what we're looking at.
And I closed everything down there and we'll focus on the nav bar inside here.
Quite a busy component, and we want to get hold of our basket from our Redux store.
Now, if we go and take a look at our store and open up the Redux dev tools, then our baskets, if
I go to the Redux tools and just go to the latest element and click on States and let's take a look
at our basket.
It's in the queries, it's in the fetch baskets.
It's in the data.
And inside here we've got our items that we can query.
Now that's kind of buried inside here.
So the way that we get this, we don't use a use selector or anything to get hold of this.
What we actually do, which may seem a little bit weird, but please remember that react query is caching
our data, so we're not going to go out and actually fetch the basket unless we have to.
At this point.
So we're going to use const and open curly brackets.
We're going to use to get hold of our basket the fetch basket query.
And like I say remember that we've got caching going on here.
So this is not going to make a request to the API unless it does not have the basket in our store.
So we'll specify data and let's give it a name of basket that we can use inside this component.
So all we really need from this data is the item count.
So I'll create a const called item count.
And we're going to get that from our basket dot items.
And effectively we just need to count up how many items there are including the quantities.
So if I have two boards plus one hat then that would be three of course.
So we need to get our array of items to return a single number that calculates the number of items added
together, including their quantities.
Now, the way we do that in or with an array is to use the reduce function.
And this effectively reduces an array into a single value.
The way we're going to use it now we give this or we open parentheses and we give this a callback function.
And the first argument is the previous value.
And that's going to be the current count of how many items there are.
And then we give it the current value which is going to be the item.
And then we execute a callback function on each item in the array.
So for this example, let's say the first argument the previous value is going to be the sum, the number
we're currently up to and the current value.
What we're executing the callback function on is going to be the item.
And then to the right of the parentheses we'll specify some plus item dot quantity.
And then we give it an initial value which is going to start at zero.
And just to ensure the item count always has a value because we've got the optional chaining there,
we need to give this an alternative just in case there are no basket items.
So we'll add an Or condition and set this to zero.
And then we can make use of our item count inside our navbar.
And where we've got our badge content equals four.
We can swap this now for the item count.
And if we go and take a look at our progress, then what we should find is that we have the correct
number of items listed up here.
I've got six as my count.
And that's two, three, four, five, six, which is correct.
Great.
So let's go back to our catalog and let's add another item to our cart.
And let's just add another board.
So I'll add this to the cart.
But what we see there is we see loading twice.
And then our shopping cart gets updated.
And it's not as responsive as I'd like it to be.
So I did mention we were going to take a look at a better way of mutating state, because I would like
that to happen instantly when I click and add to cart button.
I would like this to update straight away, and what we're going to take a look at is optimistic updating.
So we make the updates to the component.
We'll see that happening.
And if we're successful that update stays in place.
But if that update fails for whatever reason then we undo the changes.
And in this case it would go back to 6 or 7, in this case what it currently is.
So let's take a look at the slightly more complex way of updating our state in our global store for
the baskets.
And we're going to need to do a couple of things here.
So let's go to the baskets API.
First of all.
And what we're going to adjust is this on query started method this time, because we do need to know
the product and the quantity in order to achieve this.
So I'll remove the underscore and replace that with product and not product ID but the quantity.
And because I need access to the product in order to update the cart, that means the product ID is
not sufficient anymore.
For the ad basket item method.
We're going to have to pass the full product in to do what we need to do here.
So instead of product ID being a number, I'm going to specify product of type product inside here.
And then for the query this also changes to products.
And instead of product ID there we can use the product.id for the query string parameter.
Before we get to the try block let's add some extra code.
Now we'll create a const and we'll call it patch results And we're going to equal dispatch because we're
going to update our store at this point.
Open parentheses.
And I'll just do this on the next line because it gets a little bit busy.
We're going to use our basket API dot util.
And we're going to use the update query data method this time.
And the method we want to use is the fetch basket.
And TypeScript is aware of the methods available inside our components.
The next parameter is the arguments.
And this is just going to be undefined.
We don't need any arguments here.
And then the next parameter is the recipe.
So I'm going to specify as a name for this recipe draft and add an arrow and open curly brackets.
So first of all we need to get hold of the existing item if it exists.
Because if we're adding an item to our cart, it may already be in our cart.
And we just want to increase the quantity or we need to add a new item.
So we'll say const existing item equals draft dot items dot find.
And we'll look for the item and look for it by its product ID and see if that's equal to the product
ID we're passing in as a parameter there.
If we do have the existing item then we can specify existing item dot quantity plus equals quantity.
And then we'll add the else condition.
And we'll say draft dot items dot push.
And then we need to create effectively a new basket item.
And the easiest way probably is to go back to our basket model.
So open the basket TS because I would like to say new basket item.
but we can't do that with a type.
We can't create a new instance of something just using a type.
We would need a class for that.
So I'm actually going to swap what we currently have here, which is a type, and I'm going to swap
it for a class that we can use a constructor with.
So instead of type I'll say class of item, we'll remove the equals sign and just have an opening curly
brackets.
And inside our class I'm going to create a constructor.
So I'll use the constructor.
And I'm going to specify the products as one of its arguments.
When we create a new basket item I'll say products of type products, and we'll have the quantity.
And that's going to be a number.
And I'll open curly brackets here.
And then the way that we assign properties here is we'll say list dot product ID is equal to product.id.
And then we just go through each property in here and do the same thing.
So product name for the second one for this name, then we'll have Listprice equals products dot price.
And we'll have this dot picture URL equals product dot picture URL.
And say this dot brand equals products dot brand.
And this dot type equals product dot type.
And finally this dot quantity equals quantity that we're passing in as an argument.
And because we're initializing these in the constructor, all the warnings we had earlier went away.
And we can use the item like I say as a type.
So we don't need to change any of our other code.
But the advantage of this is that I can go back to the basket API now, and I want to push a new basket
item into the draft of the items.
And I can simply say that we want a new basket or a new item we get from API models Basket and then
open parentheses.
And we can just pass in the products and the quantity as parameters there.
So because we're dispatching at this level we're using dispatch here after the query has started.
So this is before we've gone out to our API.
We're simply updating our state inside here.
So we're going to see our user interface update straight away.
Then we'll use the query fulfilled and we'll remove the dispatch inside here.
We're doing that above now.
But if we do get an error then we need to undo what we're doing inside here inside this dispatch.
And that's why we created a variable for patch results.
Because we can use our patch results and then undo which will undo any changes if we do get an error
inside here.
And because we've made a change to the method we're using here, instead of taking the product ID,
we're taking the product we need to go where we've used the ad basket item.
So let's go back to our product cards.
And inside here we've got an error.
Of course we need to pass the product instead of the product ID.
So let's remove the ID from that.
And because the name is the same we can actually just shorthand this as well.
If both sides of this colon are the same then we don't need to specify product colon product.
We can just specify product there.
So now we should be able to go and test this functionality.
What I would expect to see is when I do try and add a product, I'd expect to see this update instantly
loading take place.
And then I would also expect to see the shopping cart be updated here in the nav bar.
And also when we click on this to go to the cart.
So let's give that a go.
I'll just click on this Angular Speedster boards and add this to cart.
We see the loading and I wasn't paying attention to the navbar.
So let's give that another go.
Add to cart and we see that update instantly before loading has finished.
What we should also find if we click on the shopping cart is that we don't do any loading.
We should see the product inside here.
I can see this.
I've got two of these.
If I go back to the catalog and I add the same product in again, add to cart, go back to the shopping
cart.
I should see that this is now updated to three.
So that's an optimistic update.
And what happens if we return a bad request from our API server.
And just to demonstrate that just so we can see the result of it rolling back to its previous value,
if I go to the basket controller and let's find the add basket item and let's see, I'll just return
from here early on a bad request and just say just testing, just as a very quick example.
And take a look at my API controller and see if it needs reloading.
It says hot reload succeeded.
So let's give that a go, as I don't really want to restart the API if I don't have to for this quick
demo, but I will just refresh the browser.
I believe that was required when I was testing this.
And let's see.
So I'm going to try and add a product to the cart.
We'll keep an eye on the nav bar and we can see that it did go up to 11.
But then it removed that change and went back to ten.
And we see the toast.
I'll try that again.
Go up to 11.
Back down to ten.
We see the toast.
Perfect.
So let's go back to our API.
Let's just remove that bad request that we added to the basket controller to put things back to normal
and great.
So now we've got that functionality in place.
But it's not perfect yet.
We will need to come back and make another adjustment.
If we don't have a shopping cart, then that will not work for the first item that we add.
I'll deal with that soon.
So if you do empty out your basket, or you clear your cookie and you're adding your first product,
that's not going to work just yet.
We'll sort it out soon, but next we're going to deal with removing items from the cart because it's
getting quite full.
I've got ten items in there now, so we'll look at that next.