Narrator: In this video, we will implement
the controller for the recipe section.
And this is actually a pretty cool lecture,
where you will get some new knowledge
and so you will learn how to read data from the page URL
by responding to the hash change event,
and also how to add the same event listener
to multiple events.
So, probably quite a long video, so let's get started.
So in order to figure out how to implement the controller,
let's think again about how we implemented
the search functionality.
So what we have here is that whenever we click
on one of these recipes,
we get the ID of the recipe right here in the URL bar,
right?
So take a look at this here whenever
I click on these different recipes.
So we see that the ID there changes.
Now, inside this URL, this entire part here
is called the hash, okay?
So it's the hash symbol and then whatever comes after it.
So this here is the hash,
and we can then actually make use of the fact
that we change these hashes here
whenever we click on one of these different recipes
by using the hash change event in javascript, okay?
So there's an event that's called hash change
that is fired off each time that the hash that we have
in the URL changes to something else,
And so let's go ahead and use that event,
So, this was just for testing here.
And so let's now add an event listener.
In this one, we will actually add to the global object
in javascript so in the browser,
which is Window,
so window.addeventListener and then the hash change,
And what we want to call here,
is the control recipe function, which we didn't write yet,
but that's what we're going to do next,
So we have the control search function here,
so now let's create control search.
So first thing that we should do in here
is to actually get that hash okay?
So I'm calling this one the ID,
and we can get it from window.location, okay.
Window.location is the entire URL,
and if we then use the .hash property on it,
well, that's then just the hash.
So let's try that one out, console.log, the ID.
So remember that we have this event listener here,
whenever we change the hash,
which will then call the control recipe,
which will log the current hash to console.
Of course, we need some examples here again.
So, here we go.
So take a look at this,
where the ID of the one of the search items,
then I click here,
which is this one,
will be translated into the console, right?
So you see that?
Now if I click twice on the same,
then nothing appears because we didn't change the hash,
great and so the event is not fired.
But each time I click on a new one,
then the ID is here of course in the URL in this hash,
and then also down here.
So, great, that part already working.
Now what were really interested in
is only this part,
So only the ID without the hash.
So let's take care of that.
And we can simple do a replace
because this is a string and so we can of course
call all the string methods on it.
And we will then replace the hash with nothing.
Okay, and with that the hash symbol will be gone.
Okay, so let's now go ahead and do something with this ID.
And first off, we need to change if we actually have an ID.
Okay, because imagine that we would simply do this,
so get rid of everything,
then we don't have any ID,
and so we don't want to create a recipe object.
So we only do all this logic that we're writing next
in case we actually have an ID.
So let's add some comments here,
just like we've done before.
So get the ID from the URL
Prepare the UI for changes
So this is similar to what we had before again.
Create a new recipe object of course.
Then get the recipe data.
Then we need to call our functions that
we created for servings at the time.
So calcTime and calcServings.
So we need to call them here as well.
And finally when we have all of this in place,
we can then render the recipe.
Okay so let's fill this here up with
whatever we already have.
So prepare UI for changes,
we don't have any UI methods just yet,
but what we can do here is to create the new recipe object.
And of course again we will put this in our state.
So we already have to search inside of state
and next we will put then the recipe.
So state.recipe will be a new object based on the recipe.
And of course, what we pass in here is then our ID.
Next up, let's get the data from the server,
and so we need to call get recipe, right?
So that one is state.recipe.getRecipe
Now of course we want this to happen asynchronously,
so in the background and in a way that the rest of the code
is only executed after we get back with the data.
So we are going to use, of course, "await" here.
And don't forget to then change the function
to an async function.
So yet another async await function here
so that we can load our recipe data in the background.
Next up, state.recipe.calcServings,
all that starts with calcTime.
Okay and finally we can then render the recipe into the UI.
We can not do that just yet,
but let's simply log the object, okay?
So state.recipe because for now
we don't have any UI methods just yet.
So let's quickly go over that again.
So we start by getting the ID from the URL
by using window.location.hash
and then replace the hash symbol with nothing
so that instead of the hash
we just end up with the actual ID that we need
for the search.
Then, we create a new recipe object based on a model
that we coded up in the last lecture.
Save it in our state
just like we did with the search.
So again all of our data is stored in once central place,
which is the state,
that we can then get access to.
Then after that we call getRecipe here,
which, due to the fact that it's an async function,
will return a promise.
And so therefore, in here we use await
to wait for the promise to get back with the resolved value.
So here the code will stop executing
and then after that
we will simply calculate the time, the servings,
and log everything to the console.
So let's test that one out.
Searching for pizza again.
And let's try this one, Pizza Dip.
So we get the ID of course
and then our recipe.
And all the data is actually here.
So that's amazing right?
So we can, right now,
select recipes from our search list
and it will already display the data here in the console.
So here's the next one
and if we click another one
then of course it will work as well.
And so great, that part is already working.
No imagine that we would reload this page here,
then nothing happens,
because again this only happens
whenever the hash actually changes.
And so we need to fix that,
because imagine that the user saves this URL here,
as a bookmark for example,
and then when he comes back nothing happens really.
And so what we need to do also is to add an event listener
to the load event,
which basically fires whenever the page is loaded.
So window.addEventListener,
load, and then controlRecipe.
Now I promised you in the beginning of the video
that I would show you how to basically,
add the same event listener to different events.
And that's useful here
because were calling the same function here
for these two events.
So let me show that to you.
And it's actually very simple,
we just need to think a little bit about it.
So let's comment out these two.
And what I'm going to do, is to do an array
with these two events.
So hashchange and load.
And then I will simply add a loop to it.
So a forEach, where each of the elements is an event
and I will then do window.addEventListener
and then in here we will pass the event.
So event and then of course
Recipe, and that's it.
So we went from two lines here
to just one line of code.
Now if we had like 10 lines with these events,
then of course it would become even more useful.
So let's get rid of this here,
and I hope this made sense to you.
So again we saved the strings for these two event types
into an array
and then looped over them
and called window.eventListener
for each of them passing in the corresponding event.
So let's check it out now.
So let's reload it
and you see that now it's happening.
So the load even is now doing its work here
and it doesn't only work
in case that the hash actually changes
but also if the page loads.
And so now it's even more important
that we have that if statement
which checks if there is actually an ID
because right now there is not an ID.
And so we, of course,
don't want any of this here to happen.
Now finally,
and this is something that we didn't do
in the other controller,
is that this await here can of course go wrong.
So right now we're assuming that this promise here
will always resolve.
But of course this can get rejected.
So we actually talked about that before in the last lecture
where I intentionally put a wrong key here.
So in that case,
the promise rejects.
And this will of course then also have an effect here
on this async await.
So in order to handle that event here as well,
let's add another try catch here.
So we actually
all of this we only want to happen
in case that everything goes well, right?
And so let's just alert like "Error processing recipe!"
because of course we only want to render,
for example, this recipe in case everything goes well
with getting the data from the server.
So let's introduce an error here again.
So a wrong key which will then trigger an error.
So.
It says click on one of these,
and of course it says "something went wrong"
which comes from the first catch.
And then again "Error processing recipe!"
which comes from the second catch.
So basically the first one comes from this
catch block here.
And so then this entire promise here will reject.
And so we will then also this catch block here,
which will then trigger the second alert.
Of course, let's go back to the way it was before.
And the error handling could of course be a bit better.
Instead of having these alerts,
we should add some messages to the user interface
but that's a bit too much for this simple example,
and so I'm not going to go that way.
Of course you can implement that yourself
if you feel like it.
So that's it for this recipe control.
Now since were at it,
let's implement the same thing here
in this search controller,
because we didn't do it up here.
So again, we only want to render the results here
in case everything goes right
with getting the search results from the server.
So try.
And let's move all of these.
And catch the error.
And something else that we want to do here then,
is to still clear the loader.
So we don't want to render the results,
but we still want the loader to go away
in case that there is an error.
So that should be it.
Everything is working.
And so next up,
let's actually continue building our model here
because now, we need to process these ingredients.
Right now it comes back as an array
with just 11 strings,
but we want this to be a bit better.
So in the next lecture we're going to take care of that.