Instructor: As a last step in a search functionality,
let's now implement the search results pagination.
And in this lecture,
you will actually learn some cool new stuff,
including the super useful closest method
for doing event handling in a much easier way and also how
and why to use data attributes in order
to read data from HTML5 elements.
And so let me first show you
what we're actually trying to achieve.
So I'm going to my working example here.
And so what we have done here is this button
which has page two.
When we click it, then we have the next 10 results.
We can go back to page one and to the next page, page three,
and then we have the next 10 results.
And then of course we can go back all the way to page one.
And so let's now go ahead and implement that in our code.
And we will implement this functionality in three steps.
So the first step is to change the render results function,
because that's the function that will be called
whenever we click on one of the buttons.
Okay?
So we not only need to pass in the recipes now,
but we also need to pass in the page
that we want to display.
And we can also say that we want
to pass in the results per page.
Okay?
And we can set these here as a default to 10, let's say.
And we can also start with page number one as a default.
But of course, we can then override it.
Okay?
Make sense?
So imagine we click on the button to go to page number two.
Then these render results will be called
and page will be set to two.
Okay?
Then the second step that we need to implement later
is to render these buttons actually on the interface.
And then the third step will be
to attach some event handlers to these buttons
in order to actually make the functionality work.
Okay?
But let's start with changing some stuff here
in the render results.
And it's actually not really difficult.
So what we want to do here is to not display all 30,
but only the number of results per page.
So in this case here, 10.
Okay?
And so let's define a start variably.
Let's set it here to an arbitrary number for now,
and we're going to implement this logic here later.
Okay?
But for now, let's just put some random numbers here.
So we have a start and an end variable.
Let's set this one here to 10.
So we enter zero.
Okay?
So then I can now show you
what we actually need these variables for.
So what we're gonna do then,
after determining which should be the first recipe to show
and which one is the last,
well then we can simply take a part of the total array.
So remember that this recipes here
is the array of all the 30 recipes.
So we can now take just a part of this,
which is done using the slice method.
And I think we've used this before
in one of the other projects.
And so the first parameter that we need to pass in
is the first point where we want to start,
basically copying the array.
And that's at start.
And well you can guess it.
The second parameter is for end.
Let me actually show that to you just so you
can become more familiar with the documentation.
So I usually always like to use whatever I'm searching for
and then mdn,
which is in my opinion the best web documentation out there.
Okay?
So you see slice returns a shallow copy
of the portion of an array into a new array.
Okay?
And so we can pass in the begin and the end,
which is exactly what we did.
And both of them are zero based indexes.
Okay?
So how do we actually calculate the start and the end?
And the best way to figure it out is with an example.
So let's say you're on page one.
And so you want to start at zero.
Right?
Because the index in the array is zero based,
and so the first position is zero.
So on page one, you want to start on zero,
and you want to end on number nine.
Right?
And with that, we will select 10 element.
Now if we were on page number two,
well then we would like to start
with number 10 here like this.
Okay?
So how do we implement this?
Well we can simply say page minus one
times the results per page.
Right?
And that's it.
So again, on page number one, it will be one minus one zero.
And so we will start on zero.
On page number two,
it will be two minus one is one times 10.
So we will start on page number 10.
And of course on page number three,
it will be two times 10, and so we start at 20.
And this will actually work
for whatever page you pass in here.
So we could hard code it specifically
just for page two and three,
because we know that right now from the api,
we only get 30 results.
But imagine that the api would change in the future,
and we would get a hundred results.
And then this code would no longer work.
And if you do it like this dynamically,
then everything's going to work.
Okay?
And then the end is actually just the page
times the results per page.
And that's it.
So on page one will be one times 10.
So 10 and then 20 and 30.
And what this means is that's actually the end number here
is not actually included in the new array.
So let's take a look at that here in the doc again.
So here's what I was saying.
So slice extracts up to but not including the end.
And so when we put 10 here,
it will really only copy until nine,
which is exactly what we want, so 10 results.
Okay?
Make sense?
So let's actually now go ahead and test this.
And if we do it right now,
it will actually already work, I'm sure.
So if we now type in pizza, we will only get 10 results.
So let's wait for it.
And let's count them actually.
Some more space here.
So one, two, three, four, five,
six, seven, eight, nine, and 10.
Perfect.
Let's change it here now as a default to three.
And actually, let's also change this one here to five
just to see if everything works.
And indeed, we get five results.
Put it back to 10.
Let's check it out again.
And indeed, here we go.
Alright.
So that's now implemented.
Let's now take care of rendering the actual buttons.
So let's write a function.
Render buttons.
And you see that this is, again, a private function.
And that's because we will call this function
from the render results.
Okay?
So what do we actually need here?
Well let's put in just the page for now,
and we're gonna add more arguments
later down the road if we need them.
Okay?
So of course we could have done this functionality here
so the rendering of the buttons
ride inside the render results,
but again,
I like to keep different stuff in different functions here,
so to make it more reusable and more maintainable.
So the goal of this function here is to render the buttons
according to the number of the page that we're on.
So let me show it to you again
how it should work in the end.
So when we're on page one,
then it should only display the button for next.
If we're on the second page,
so not the first one and also not the last one,
then we have both buttons to go back and also to go forward.
And when we're on the last page,
well then we only want the button to go back.
Okay?
So how do we actually now implement this?
Well we need to know on which page we are
and also how many pages there actually are.
And how do we actually know how many pages there are?
Well, all we have to do is to divide the number
of results by the results that are displayed per page.
Right?
So we need to pass in that data to our function.
So we need to num of results,
and we need how many results we have per page.
Right?
Does that make sense?
Let's call this one here pages,
which will mean, again, how many pages there actually are.
And we calculate this by doing the number of results
divided by the results per page.
So in our example, we have 30 results,
and on each page, we have 10 results.
And so 30 divided by 10 will be three.
And so now we know that we have three pages.
Now imagine that for some reason this changes here again.
So if the api changes in the future,
and instead of 30, we get back 45, for example.
Then 45 divided by 10 would be 4.5 pages,
and we don't want that of course.
Right?
So we want not 4.5, but in this case,
we want five pages in order to have room
to display all of the results.
And so we have to use a function
which is part of the math object, and it's called math ceil.
And so this will basically round it to the ceiling,
so to the next integer.
So if we had 4.1, then it would round it up to five.
If we had 4.4, it would also round it up to five.
Because if we used normal rounding, then well,
4.4 would be just four page,
because it would be rounding down.
Right?
But we always want it to the ceiling.
Okay?
So we have three pages.
And so let's now do some if else statements.
So saying what we just said when we looked at
what buttons we want available on each page.
So if we are on page one, so page is one,
well then we want the button to go to next page.
So for now, I'm just writing it down here,
so that we can then know what to do.
Else if.
So if we're on the last page,
we only want the button to go to the previous page.
Right?
So how do we say the last page?
Well, it's just that the page is equal to pages.
Right?
So if we have five pages, and we are on page five,
well, it means that we're on the last page.
Right?
Here let's say only button to go to next page.
And here we only want the button to go to the previous page.
So these are the two edge cases,
but then we also have the case
that we are in one of the middle pages.
And so that means that the page is less than pages.
Right?
So in this case, we want both buttons.
Okay?
Now imagine that we only have one page.
Then in that case,
we actually don't really want to display any buttons at all.
Right?
So let's say here that the page should be one
and pages should be greater than one.
Okay?
Which means that there are actually more than one page.
And the same thing applies to the last case.
So in case that we're on the last page,
well we really only want a previous button
if there are more than one pages.
And so let's now actually write the code for the buttons.
And you see that we will have a lot of duplicate code here.
Right?
Because we will have a button here,
and here we will actually have both buttons,
and here we will again have a button.
And so all of this HTML code
will actually be pretty similar.
And so what we can do is to, once more,
export it to an outside function.
Okay?
So let's do that.
Const create button.
So what do we need to pass into this function
which is going to render the button?
Well we need to pass in the number of the page
that we're in so that it can then print
that number to the interface.
And we will have to say if it's a button
to go back or to go forward.
And so let's call that one the type.
Let's now go to the HTML and actually grab the button here,
because it should already be somewhere here.
And let's actually get this here as a comment.
And now before we paste it here,
I can see that we can actually get rid of these parentheses,
because all we really want to do from this function here
is to return the markup for this button.
Okay?
So we are going to render it down here
inside of the render button functions.
Okay?
And so from here,
we are actually only going to create the markup.
And so we just want to return a template string
from this one here.
And so this is now just some regular HTML here as a comment,
as you know already.
Okay?
So this is just so that we see what's the difference
between the previous and the next button.
So you see here that we have the results button previous
and results button next.
So that's one of the differences and also here in the icon.
So here we have left, and here we have right.
Okay?
But that's actually all there is.
So let's go ahead and copy this one here,
and that's not correct.
Alright.
So let's start with the number here.
So imagine that we're on page number two
and that we want a previous button.
So in that case, we want to say page number one here.
Right?
And in case we are on a next button,
well, then we actually want page number three.
And so depending here on this type,
we can now specify what we want to display here
in this page number.
So let's say that as the type,
it can either be previous, or it can be next.
Okay?
And both as strings of course.
So in here, whenever we call this create button,
we're going to pass in the number of the page
that we're currently in
and either if it's a previous or a next button.
Okay?
And so let's now use the ternary operator here
in order to make an if else statement,
but in just a single line.
Okay?
So we can say if the type is previous,
so previous,
well then what we want is page minus one.
And if it's not, then we want page plus one.
Okay?
Make sense?
So imagine we have a previous button,
and we're on page number two.
Well then we want to display that we want
to go to page number one.
So two minus one.
But if we have a next button,
well then on that button it should say page number three.
And so that's page plus one.
Okay?
Next up, we can do the same here for the icon.
So we have left and right.
So again, if it's a previous button,
then the arrow should point to the left side.
And in the other case, it should point to the right side.
Okay?
And so that icon here coming from the icons that svg
will then display exactly that what we want to do.
Next up, we have this class name up here.
So results button previous for the previous button
and next for the next button.
So here, we can simply plug in this type.
So let's get rid of this,
and just say type.
Simple as that.
And so that's almost at.
There's just one more thing that we need to do here,
which is to actually plug in the data into this button
containing the number of the page where we want to move
whenever we click this button.
Okay?
So basically we need to encode this number here.
And I can actually copy it right now
so that it's stored inside of this element,
inside this component.
And so in HTML5,
there's something called the data attributes.
And so we can say data,
and then we can specify a random name.
So like go to.
So go to page.
So the page that we want to go to.
And then, let's simple paste in here the number
that we want to be here.
Okay?
And we're then going to use this property here later
when we attach the event handler to these buttons.
Okay?
So just keep in mind that we're going
to use this one here later,
and by then I'm going to explain to you why.
Now all we need to do is to call, actually,
this function here in all of these different cases.
Okay?
So create button with the current page and 10.
In this case here, we want the prev,
so the previous.
Right?
Actually no, we want the next, of course.
From page number one, we want to go to the next page.
Now this here should then be assigned to a variable.
Right?
Now we cannot use a const here.
Right?
So we cannot say const button.
And that's because const and let,
so the new ways of declaring variables in ES6,
they are block scoped.
And so they are only available inside of this block.
Okay?
And so I have to declare it inside of this block.
Well then button is not going
to be available outside of this block.
So what I have to do instead is
to declare the variable outside with a let,
because we will want to change it.
Okay?
So we cannot use const here,
because we then want to reassign it.
Okay?
So now we can say button in here.
We can say button here, but with prev.
And then we can also say button in here.
But here remember, we actually want both buttons.
And I'm not sure why I deleted the comments.
Let's go back here.
So prev, and here we want both buttons.
So now we need a string which contains one button
for next and one for prev.
So let's do that.
So a template string again here.
I'm not sure why this one closed.
So one for next and one for previous.
Okay?
So I created a string which contains the result
of calling these function here two times,
so these two strings.
And so we will then have these two buttons.
Okay?
So all we need to do now is to actually
then insert this new element right into our dumb.
And so where do we actually want this?
Well let's go back to our HTML,
and well, it should be inside
of this div here called results pages.
So let's edit to our element.
So we have search res and search res list.
Let's now add search res and pages.
So elements dot search res pages dot insert adjacent,
and not element but HTML.
We want it at after begin,
so right at the beginning of the element
and then of course the element we want to insert,
which is of course the button.
Whew.
So that was a lot of code.
Let's actually take a look if this is working.
Okay?
So this is our result.
Oh and I see an error here already,
and it is in base dot js.
Let's take a look.
And actually it is a red here.
It the code editor,
it was already telling me something's wrong,
and for some reason I added this semicolon,
which I shouldn't.
And so let's see if it works now.
Gives us some more space.
And I do not see any buttons down here.
And yeah of course I don't,
because we did never call the, where is it?
So we did never call the render buttons.
Okay?
So let's go ahead and do that.
So some comments here.
Render results of current page.
And render the pagination buttons.
So render buttons with the page,
which is page the number of results
is the length of the recipe.
Right?
So recipes is the array which contains all of the recipes.
And length then gives us the amount
of recipes that are in there.
And then finally, the third one is results per page,
which is just results per page,
which is this one that we get as an input to this function.
And so let's take a look at that again.
And so well, something is not right here.
And I guess, yeah,
the problem is that we're still on page three.
So of course we always want to start on page number one.
And so here we go, page two.
Well the icon is not really at the right place,
but we're gonna take care of that in a second.
And then of course if I click,
then it doesn't do anything yet,
because we didn't yet attach the handler.
Let's just inspect this element here
so that I can show you this data attribute that we added,
and so this one here.
So we have to data dot go to, which is set to two.
And so next when we're adding the event listeners,
this is how we'll know which page we should move to.
Okay?
So instead of reading it from the text,
from this dumb element here,
we will simply read this property here,
which is a lot better.
Okay?
So it works on page number one.
So let's hope it also works on page number two.
And we're gonna test that
once we have our event handlers properly working.
And so that's what we're going to do next actually.
So that's going to be in the controller again.
So in a controller is where we have all our event listeners,
and we then control what should happen
based on what's clicked, of course.
Now in this case, we actually have a small problem
because where do we actually attach these event listeners to
if the pagination buttons are not yet there
when the page is loaded?
And so we have to use something
called event delegation again.
So we used that before in the course.
And so the concept is that we attach the event listener
to an element that is already there,
and then we try to figure out where the click happened
so that we can then take action based on that.
Okay?
And so the element that is actually already available
at load is this one, so results page,
which is where we put the buttons into.
And so that's where we have to put our event handler.
So elements dot search res, and it is here, pages.
Add event listener on click.
We want to have access to the event
so that we can then use the target property,
remember that one.
And so that's actually log it to the console.
So e dot target.
So e is our event here, our click event in this case,
and then the target.
So exactly where this click happened.
Now in order to be able to view two buttons
and not just one,
let's go to page two,
because then we should have the previous and next button.
So let's test it out.
And indeed, we have page one and page three.
And so let's now click on the buttons.
And you see that here now we have the element
where the click happened.
Now I clicked here on this text.
Now watch what happens when I click on the icon.
You see that we then get the icon.
And if I click here outside (mumbles) on the button,
well then we get something else.
And so how do we actually now define
where we want the click to happen?
Or in other words, how do I say that I want something
to happen when I click on this button
when in reality I'm clicking at this text
or maybe even at this icon
and not really the button element itself
that I'm looking for?
So how can we do that?
Well we actually have something that helps us in JavaScript
for dealing with just that.
And that is the closest method.
So again, let me try to show that to you.
So it works on all dumb elements,
and you see that closest returns to closest ancestor
of the current element which matches the selector
given in the parameter.
Okay?
So let's see that in action.
I'm going to create a new variable here actually.
So let's say button.
So e the target.
And now the closest.
And so now in here we put a selector.
And the selector that we want is the class of the button
that we're actually looking for.
So let's go back here.
And so what we're looking for, and well now it's gone.
So I cannot show it to you anymore.
But the button that we're looking for
is the one with the button inline class.
Right?
So I showed you that we sometimes click
on this svg here or on this span,
but really we're only interested
in clicks to the button itself.
And so we can now use the closest method and say
that we are only interested in the ones
with the class of button inline.
Okay?
And now I should actually go ahead and save this
in the element strings here, so put it in here.
But I'm not going to do that,
because then I would have to do the same here
for this one and for this one here as well.
And so right now I'm not going to go down that road.
Okay?
So let's keep it simple here,
and right now I'm simply going to lock the button.
So what's going to happen here is that now,
well let me show it to you.
So what happens now is that I still click here
on the span or on the icon like this,
but it will then find the closest element
with the button inline class,
so this exact button that we're looking for.
So no matter where I click now,
we only get the button which is exactly
where this data here is located.
Okay?
So what we're interested for is the button
where we have stored the page number.
So this one here is page three,
and this one will of course show page one.
Okay?
And so that's the number that we're interested for,
and therefore we really need
to just select this exact element
and not any of their child elements.
Okay?
So let's now say if there is a button,
then let's read the go to page.
Okay?
And so now we're actually going to read out the data
that is stored in that data attribute that I showed you.
And it is so useful,
because all we have to do now
is to say button dot data set dot go to,
and that's it.
Let's log it to the console,
and test it out of course.
And you see it shows me one, and here three,
and that is again of course
because it saved inside of this attribute.
So we can read out this data attribute
whenever we prefix an attribute with data,
then this variable here gets stored
in a data set dot go to here in this case.
Okay?
And if it was something else,
then of course, for example if I had page here,
then it would be data set dot page here.
Okay?
And so this is very handy way of having access to data.
Now you saw here maybe that this is not really a number.
It's really a string.
'Cause a number looks like this.
So it's in a console it's showing blue.
And so we have a string here really.
And so we still have to convert it to a number.
So we can use parse int,
so it will basically convert it to an integer,
and we still have to specify the base of the number,
which is 10 here.
Right?
So 10 just means that we're on base 10,
so from zero to nine.
For example, if I were to put two,
then we were in binary,
and so we will only use the zero and the one.
But that's not the case, we are on base 10,
so let's just keep it like that.
Okay.
Now all we need to do is to use our render results method
or function.
Now all we have to do is to call it just like we did before
but specifying the page,
which by default should be one of course.
And so let's do that.
So we used it before here to render.
And here we didn't pass any number,
because we could simply pass a number one,
but that's the default anyway, so let's not do that.
So all I'm gonna do is to just copy it here.
I'm still leaving the go to page down there
so we can check it out.
And then all we need to pass in is the go to page.
And that's it.
That's all we need to do really.
So I hope this is making a ton of sense to you,
and I'm sure it is.
So let's now click on two,
and it will add these new down here below.
Okay?
We also have all of this here,
so that's not ideal of course.
So whenever we add some more results here,
we first should actually clean out the list
before we keep adding into it of course.
So we have render results,
but before we do that, let's actually use clear results.
And we did that before here again, so clear the results.
And so now it's becoming in really handy
that we have it all nicely separated
into our different functions and modules,
because we are of course
reusing all of this code from before.
And so we just have to do clear results
and render results again here.
So let's get rid of this one here.
And the other problem was with these buttons.
So we should actually also clear out the buttons.
Okay?
So let's do that and then clear results,
and it should be here.
So clear results,
and so we also have to say search res pages dot inner HTML
should be empty as well.
So try it out one more time.
So that is page one, and here we go.
Page number two with the next results,
and finally, page number three, and great, that's awesome.
So it's really working.
Also the numbers here are correct.
What's just not correct is these,
this should actually be on this left side,
and this here should be on the right side.
So I'm not really sure why that is happening,
but let's take care of that for a second.
For now, let's just appreciate that it's really working.
So it tells us to go back to page number two,
because we are on page number three.
Now we're on page number two,
and so now it says go back to one or next to three.
And then here we just have the forward button
just as we said here in our render buttons code.
So just as we did here.
Right?
And the problem with the text being on the wrong side
is just because this one here
should actually come before the icon.
Okay?
And so now everything will be perfect
just one last time before we can finish with this video,
and so now it works.
So perfect.
So I hope that wasn't all too complicated for you,
and of course when you're doing your own app,
you're not going to implement it in the exact same way.
And so all I want you to really understand
is the entire logic behind this pagination.
So basically creating the start and the end numbers
and then rendering the buttons here
according to the page that we're currently on.
Okay?
Also it's important to remember event delegation,
which is something really important again in JavaScript.
And we did that here again.
We did it on other projects,
but so now here we did it once more for you to remember it.
Okay?
Anyway, this was a huge lecture.
Great to see that you're still with me here,
and take a break now
and then see you in a second in the next one.