So let's now send that Ajax request
that will trigger the updateItemController action.
For this, we want to wire up this update button here
with a JavaScript event listener,
and for this of course, we first of all need
a JavaScript file that's executed in the browser.
And I'll name that file cart-item-management.js,
not to be confused with cart-management,
which was responsible for adding items
from inside the product detail pages.
Now I have this new file for managing the items
that are already in the cart,
and I want to include that in my cart.ejs file.
Here at the top after including the cart.css file,
I'll add a script and include
/scripts/cart-item-management.js,
and of course, add defer to make sure
that we only execute this once everything has been parsed.
Now, once we're here already,
let's take a look at cart-item.ejs and have a look
at the button at which we want to add a listener.
And actually here I'll change things a little bit.
Instead of just adding a listener to the button,
I'll turn this dev into a form
because it's not a bad idea to include
an input button combination in a form anyways,
for semantic reasons.
And instead of listening for a click on the button
I want to listen for the submission of that form.
Now then once the form is submitted,
I'll need to get access to this input here
and to the data that was entered there,
but we'll do this in a second step.
First of all, let's get access to this form here,
and for this let's not forget
that we've got this form more than once on the same page.
Every cart item has its own form.
Therefore I want to get access to all those forms
that have the cart-item-management clause on them,
and then I want to add submit event listeners
to all those forms.
Hence in the cart-item-management.js file,
we can get access to the cartItemUpdateFormElements
by using document.querySelectorAll
to select all matching elements,
and by then looking for the cart-item-management.css clause
with this CSS selector that's passed to querySelectorAll.
Then we can go through all these formElements,
so through all the cartItemUpdateFormElements,
for example with a four off loop,
and then add an event listener with addEeventListener
and listen to the submit event.
That's the goal here.
And that there is such a submit event built
into the browser, it will be triggered, guess what,
whenever that form is submitted
by clicking a button in the form.
Now we need a function that executes
when that form is submitted,
and I'll name this function updateCartItem
because that is what it'll do.
Now of course, since we use a form
in cart-item.ejs, we could of course always just
set the action and the method attributes there
and let the browser handle the form submission for us,
but I don't want to let the browser do that.
Instead I want to send my own
JavaScripted driven HTTP request
so that we don't force a page reload.
Instead we stay on the page
and just update the parts that changed,
and so that we can also send a patch HTTP request,
which otherwise wouldn't be possible
since forms by default, if the browser supports them for us,
only support get and post HTTP requests.
So that's why I want to handle it myself.
And the implication of that is that here in updateCartItem
in the cart-item-management.js file,
I first of all have to prevent the browser default.
We can do this with help
of the automatically generated end-provided event object
which we get here by then calling event.preventDefault.
With that, we prevent the default
of the browser automatically sending some kind of request,
which we don't want here.
We of course also have to connect
the addEventListener method for the submit event
to the updateCartItem function we just added,
like this.
Now we are in that function
for a certain form that was submitted,
and now of course we need to get access
to more details about which items should be updated.
To be precise, we need the product ID
of the item that should be updated
because the product ID allows us
to uniquely identify a single cart item,
since we have one cart item per product,
and we of course need to know which quantity
we want to use in the future,
so the new quantity that was entered.
So for this, we need the productId and we can get that
as we did it before, by going to cart-item.ejs
and simply adding this as a data- attribute
to the elements that causes the event,
in this case the form.
Here we can add productid and then simply output
item.product.id like this.
Then we can access this.
We also can already add the csrf token
because we'll need that as well.
So I'll add a data-csrf field here,
and then here, output locals.csrfToken,
and then back in our JavaScript file,
in the cart-item-management file,
here we can get access to the form
by accessing event.target,
because that's the thing that causes event,
the form that was submitted.
And on that form we can access the data set
and get access to the productid, with a lowercase "i" here,
because I chose a lowercase "i" in the cart-item.ejs file
for data-productID here.
And of course, we also want to get the csrf token already,
so that is then accessed with help of form.dataset.csrf,
not csrfToken, just csrf-
that was the identifier I added as a data attribute-
and of course we need to know the quantity,
the new quantity or the quantity in general
for that item that should be set,
and we get that from the input element
that we have in this form.
Now if we have a look at this form,
we see that the input element is the first element in there.
So we can do a little bit of DOM traversal to get hold of it
since we already have access to the form.
So the quantity can then be extracted
by accessing form.firstElementChild.value.
The first element child will be the input
and every input Dom element has a value property
that holds the value that was entered by the user.
Now we've got all the data we need to send the request
and therefore now we can again use the fetch function
to send the request to /cart/items
because that was this route which I defined here.
Now it's a patch request which I want to send,
so therefore for this configuration object,
which we pass to fetch,
we set the method to PATCH
and we now need to add a body
and that needs to be in JSON format
just as we used it before for the post request.
And I'll send the object where I have a productId,
which is the productId I extracted up here,
where I add the newQuantity,
which is my quantity constant I have here.
And the keys I set here in this object
should be the keys I'm trying to extract in my controller.
So there in the controller,
I try to extract productId with a capital "I" and quantity,
therefore I have to set those keys in cart-item-management
and therefore this should actually just be quantity
because as we just saw,
I try to extract quantity in the controller,
not newQuantity.
Last but not least for the csrf protection,
we need to add the _csrf field
and set this equal to the csrfToken we get access to.
Now we also need to add some headers-
to be precise, the 'Content-Type' header
so that on the server side
our content is parsed correctly, as explained earlier.
We set the 'Content-Type' to 'application/json' for that.
And then just as before, this of course returns a promise,
so we can use async await here to wait for this response
and store that in an extra constant,
and just as before,
we of course also might want to do some error handling.
We can move this all into a try block here, like this,
and catch any potential errors we might be facing,
in which case I'll just use the alert function
to build in one and say 'Something went wrong!',
and then return so that no other code executes,
and to ensure that we can use the response thereafter,
just as before I'll define it as a separate variable,
which I set in here.
And then therefore, after this try-catch block,
we also want to check if the response is maybe not okay,
so we have no technical problem,
but we have an error status code
that's sent back for example.
In this case I also of course want to do something,
I want to simply, well, do to same as before-
show this alert and return.
After the safe check, we know that the response is okay,
and then we can get the responseData
by awaiting response.json to extract that data,
and then once we got this data,
we can of course update the Dom appropriately.