مرور اسکرول نامحدود0:00
Welcome back. All right, so now that we understand lazy loading, I bet if we're clever, we can find a way to create an infinite scrolling feature by leveraging another new feature in Inertia called prop merging. Let's have a look. So here we once again have our dummy courses page, but this time I have numbered them to make it crystal clear where we are in the pagination, right? So if I scroll to the very bottom, we are loading all 100 dummy courses here. All right, so if you use Laravel,
Paginating Courses Data0:28
So if I scroll to the very bottom, we are loading all 100 dummy Course courses here. All right, so if you use Laravel, you know that we can at least paginate these results in a traditional way. I could swap out my query to say Course::paginate(10), and let's paginate them into sets of 10. But now, of course, when I make this change, the data that's being passed to the client changes a little bit. Now we're passing through an object where the course items are actually a property on that object. So if I switch back now, of course, it fails.
where the course items are actually a property on that object. So if I switch back now, of course, it fails. We could even try this out by visiting the dashboard, opening up the network tab, and yeah, if I click on it, you'll see here's what we get. So we have our courses, and now this is an object where once again, the items or the collection we care about is contained within a data property. Now while we're here though, notice there's a bunch of things that might be helpful to us,
Now while we're here though, notice there's a bunch of things that might be helpful to us, pagination-related values. So I have a list of links, and this would be really helpful for rendering traditional paginated buttons, but we're not doing that in this case. But we also have information like what is the current page we're on, what would be the next page we should visit,
what is the current page we're on, what would be the next page we should visit, what should be the very first page, and things like that. Very, very helpful. All right, so if I wanted to fix this issue, I would switch back to my view view, my view view, spelled differently, and I would say loop over the courses.data collection. And now if I give it a refresh, it works.
and I would say loop over the courses.data collection. And now if I give it a refresh, it works. Okay, so yeah, of course, because we're now using Laravel pagination, I could just append to a query string. I could say, now give me page equals 2, and that'll give me 11 through 20, page equals 3, 21 through 30. But yeah, notice it's not being merged in, and that's really what I want in this case.
Adding Load More Button2:08
But yeah, notice it's not being merged in, and that's really what I want in this case. If I start on page one, and I click on a button maybe at the bottom, I want 11 through 20 to be appended to this collection rather than replace all the items that came before it. All right, so how do we make this work? Well, at least to start, let's create that button. We're going to take this step by step. So we'll have a button that says load more.
We're going to take this step by step. So we'll have a button that says load more. And yeah, well, at least to start, if you wanted, this could be a traditional link tag. So for example, let's pull in link from Inertia, and I could say, make this into a link, and we're going to bind it to courses. And remember I said there was a, what was it, nextPageUrl property value? I think that should work.
what was it, next page URL property value? I think that should work. So let's give it a shot. We come down, and yeah, notice if I hover over it, we can see it's going to load page two. So this will give us exactly what we had before. Now we have 11 through 20, 21 through 30. Yeah, that's fine for simple, basic pagination. That is not what we want for this video. So let's switch back,
That is not what we want for this video. So let's switch back, and I'm going to bring this back to a button. I'm going to handle this programmatically. So let's say when you click on me, let's call a good old function named loadMore. Scroll to the top, create our function, loadMore. And yeah, what we might do is pull in Inertia's router, and notice it automatically got imported there. And yeah, if I wanted to reproduce what we just did,
and notice it automatically got imported there. And yeah, if I wanted to reproduce what we just did, I could say router.visitProps.courses.nextPageUrl, and that's going to do the exact same thing. So now we're just doing it via a button click event listener. All right, but yeah, it's not what we want. So it sounds like I want to refresh the current page and say, all right, give me new courses, but merge the new courses in with the existing collection of courses.
Reloading With Page Params4:02
but merge the new courses in with the existing collection of courses. So let's do this, router.reload. And I'm going to say, no, reload, but only fetch the courses. That's the only thing I care to fetch from the server. So notice topics aren't changing here, so we don't need to refetch the topics and run that query and merge those in. I don't want that. All I care about is courses in this case.
I don't want that. All I care about is courses in this case. So if I were to bring up the network tab and scroll to the bottom, if I click load more, you'll see, yep, it makes a request, and specifically, it retrieves the courses. And notice, once again, it will always include the errors because you need those, but it excludes everything else, as you can see here. But yeah, it's just fetching the exact same collection.
but it excludes everything else, as you can see here. But yeah, it's just fetching the exact same collection. So notice where we're currently on page three, and it made a request, again, to page three to get the exact same set of data. That's not what we want in this case. So why don't we override this and say, let's set the data, and we're going to do the page number, should be courses, not quite right AI, it's not courses.meta.
should be courses, not quite right AI, it's not courses.meta. So you got to be careful with AI. Half the time, it gets it wrong, but it's always uber confident in what it suggests. It's horrible and amazing at the exact same time. All right, anyways, let's go to, I'm even getting it wrong, props.courses.currentPage, I think that's right, plus one. Let's give it a shot.
props.courses.currentPage, I think that's right, plus one. Let's give it a shot. What do we have here? Let's go in, props.courses, current_page, I'm sorry, currentPage. All right, so you see what we're doing here? We are reloading the currentPage, but I'm overriding the page query string value to be what the currentPage is, plus one. Yeah, because I don't think there's an easy way
to be what the current page is, plus one. Yeah, because I don't think there's an easy way other than using one of the strings here. Yeah, it doesn't share with me what the next page number should be. That's okay. So let's give it a shot. I'm going to click load more. And yeah, now you can see it moved us to page four. All right, but same boat.
And yeah, now you can see it moved us to page four. All right, but same boat. What are we doing here? What's the problem? Well, think about it. We reloaded the current page and we updated the query string value and that hits this route again and it read what the current page was. And so Laravel gave us the patronated results.
Using Inertia Prop Merging6:25
and it read what the current page was. And so Laravel gave us the patronated results for the current page, which is four. And it re-rendered the view and courses got updated. So at no point, once again, did we merge in the new data with the old or existing data. All right, so we need to tweak how we set this up. So I'm going to swap this out with a call to inertia.merge. Nice and readable, all right? So this is a tricky one.
Nice and readable, all right? So this is a tricky one. What I'm showing you here should work, all right? And very likely at the time you watch this video in the future, in my future, this is going to work. The documentation says this should work, but it doesn't quite work. I think there's a small little bug that will be patched up very likely a day after I record this. But nonetheless, your job, if you're in the future for me
very likely a day after I record this. But nonetheless, your job, if you're in the future for me is to test this out and see if it does work out of the box. And it just might. And if it doesn't, then keep watching. All right, so let's give it a shot. Let's go back to page one and I'll show you. If I scroll down here, we've fetched page two. Notice we do have mergeProps. So there is some correspondence going on there.
Notice we do have merge props. So there is some correspondence going on there to be clear that this data should be merged in. We can see courses, data. We have items 11 through 20. But if I scroll to the top, we still lost one through 10. All right, so here's the issue. I think there's a slight little quirk with Inertia or Vue or the deep merging strategy they have in place right now. And if you have 15 seconds, there's an existing GitHub issue.
or the deep merging strategy they have in place right now. And if you have 15 seconds, there's an existing GitHub issue related to exactly what we're talking about here. So yeah, once again, very likely in a future patch release, they will fix this and it'll be no problem. But until then, the issue is, because we're passing through an object here, the deep merging strategy isn't quite working. But if we were to pass a traditional basic vanilla array, then it does get merged properly.
But if we were to pass a traditional basic vanilla array, then it does get merged properly. So right now, I'm going to skirt around that little issue by extracting this. I'll do something like this. Let's say courses. So we still paginate the results. But when I send through the items, I'm actually going to convert this to an array or a collection that becomes an array.
I'm actually going to convert this to an array or a collection that becomes an array. So I can say courses, items, just like that. Next, I still need information about the pagination, right? So we're going to send that through as part of its own property. Yeah, so you can see what we're doing here. We're just taking the fancy pagination object that has the data and the links and the next page and all of that stuff.
that has the data and the links and the next page and all of that stuff. And we're splitting them up into simple arrays so that the Inertia merge feature works as we'd expect. All right. So I can say courses. Really, I want to say, give me everything except the data. So everything that was in that initial object except the collection of data items. So why don't we use Laravel's handy dandy array class
except the collection of data items. So why don't we use Laravel's handy dandy array class to say, give me everything except, yep, AI is so smart, something just like that. All right. So let's clean this up by importing that class. And I think we're in pretty good shape here. All right. So let's return to our view and I'm going to accept the pagination here.
So let's return to our view and I'm going to accept the pagination here. And by the way, courses originally would have been an object that was appropriate, but I've already swapped that over to an array. We now accept the pagination and the topics. Okay. So now when we load more, I'm actually going to read the pagination property to increment the currentPage.
I'm actually going to read the pagination property to increment the currentPage. All right. So let's cross our fingers and see if we can make this work now. Once again, we load our courses and it doesn't work. Let's see what the issue is. Oh, my favorite kind of issue where there's nothing in the console,
Oh, my favorite kind of issue where there's nothing in the console, but it doesn't work. Think Jeffrey, think, what's the problem? Oh, I know what it is. I know what it is. Scroll down. And yeah, now because courses is just an array, there is no data property. So I can remove that.
there is no data property. So I can remove that. All right. Let's see if it works this time. There we go. It's working. So we're still crossing our fingers though. Let's see if we can make it work. Look, I even know how this video is going to go and I'm still not sure if it's going to work.
Look, I even know how this video is going to go and I'm still not sure if it's going to work. And that's programming in a nutshell. All right. So I click loadMore. We are on page one. So hopefully we load more from page two and I see it working already. If we take a look, it fetched items 11 through 20.
If we take a look, it fetched items 11 through 20 and then 11 through 20 were merged in with the existing collection. So now if I take a look at props courses, yes, we have our original. Oh, that should be reflected. There it is. Sorry. I guess that doesn't get updated.
Sorry. I guess that doesn't get updated. You can see now that courses is an array of 20 items and it's working. So now I can load more. And yeah, it's still technically traditional pagination because I have to manually click on this button. So this is just kind of a programmatic way of simple basic pagination. However, oh, that's not right.
of simple basic pagination. However, oh, that's not right. Oh, you know what it is? Did we, what did I do wrong here? Wait, hold on. Hold on. Hold on. Let's see what's going on here. 10, 11 through 20. Ah, it's not.
10, 11 through 20. Ah, it's not. Oh, of course. Of course. Okay. So that brings me to the next topic here. So did you spot what the issue was? Why does it keep loading 11 through 20? And the answer is, well, now we're never updating the currentPage number.
And the answer is, well, now we're never updating the current page number because we're programmatically doing that. So here's what we do. Come back and right at the top, when we load more, I want to refresh the courses as well as the pagination. So now we're going to fetch the updated pagination results and merge those in. Okay.
and merge those in. Okay. I was getting scared for a second there, but let's see if it works. 11 through 20. And yeah, now that works. Okay. So now if you think about it, the final step is just to remove the need for me or the user to manually click on this button.
Auto-Loading on Scroll12:53
the final step is just to remove the need for me or the user to manually click on this button. And that's where we come back to the previous episode where we learned about lazy loading as the user scrolls. What I want to do here is say, when the user basically gets to the bottom of the collection, I want to lazy load the next paginated set of data and merge those in. So if you need to, you might want to go back and watch that episode.
So if you need to, you might want to go back and watch that episode where we learn about the whenVisible component. It's episode four from this course. Otherwise, I'm just going to do it right now and you can come along. I'm going to swap this out with a new whenVisible component. And the data I want to bind this to is courses. So let's think about what's going on here.
And the data I want to bind this to is courses. So let's think about what's going on here. We're saying, all right, well, when visible and where's the section? Well, it's at the bottom of the collection of courses. So when we reach the bottom of the collection of courses, when that section is visible, then I want to render this button. Okay. But like I said,
Okay. But like I said, I don't actually want the user to click on this. So why don't we set it as a fallback? Just like this. And actually, I could even say span loading more like this. All right. So if for any reason there's a delay and we do reach this section before the courses data has been fetched,
and we do reach this section before the courses data has been fetched, the user will just see a little bit of text that says loading more. Okay. But we still have a problem. Right now, it's just going to try to refetch the current page. So take a look at this. So if I come back,
So take a look at this. So if I come back, let's go back to page one. And yeah, actually, let's do this much slower. So I'm going to bring up the network tab. And yes, once I get to the end of the collection, which is item 10, we see a request, but notice it's just fetching the current courses again. We haven't updated what the current page is.
but notice it's just fetching the current courses again. We haven't updated what the current page is. So it got the initial set, items one through 10. And if I do it again, it will... Oh, actually, no, it won't do it because by default, the way lazy loading works with Inertia is it will only trigger the reload once. But as we learned in episode four, we can add this always prop.
But as we learned in episode four, we can add this always prop. And that means now always, even if we scroll all the way back up and we come back down, that should trigger another refresh. So let's give it a shot. So many things to see. All right. So we scroll down to item 10
All right. So we scroll down to item 10 and that triggers a reload. Let's keep going. Yep, another reload and another reload, but it's just getting the same set of data over and over and over. So basically all I have to do here is reproduce this router configuration. And we can actually do that.
is reproduce this router configuration. And we can actually do that as part of the WinVisible component. Let's see if we can find it. So here is the declaration. Don't get confused. We're just looking for some props. Notice we can send through params. You learned about a buffer and that is a pixel value for
You learned about a buffer and that is a pixel value for how high before you reach the intersection should we trigger it. So you might want to do that. You might want to say, well, when we're about 500 pixels above the bottom, let's go ahead and fetch the data so that by the time the user does reach the bottom, it's ready to go.
so that by the time the user does reach the bottom, it's ready to go. Here's the always prop we sent through and there's a bunch of stuff here. But in our case, what do we need here? We're going to update the params that get passed through. So let's see what the reload options are. So reload options consists of visit options. Let's take a look at that.
So reload options consists of visit options. Let's take a look at that. All right. Yeah. So we can send through what the method should be, what the corresponding data is, only all of the stuff that you're used to when working with the basic router. Awesome. So if I come back here,
Awesome. So if I come back here, let's update this. I'm going to set the params and I'm going to bind this to an object. So once again, if we scroll up, actually, let's see if I can just steal some of this. I'm going to take all of that and paste it in here and clean up. And then I don't need to reference props.
and paste it in here and clean up. And then I don't need to reference props. Of course, when you're inside of your template HTML, you don't have to access a prop first through the props variable that we save it to. You would only have to do that as part of your script setup section. So now I'm going to get rid of this entirely. We don't need it. We don't need the router.
We don't need it. We don't need the router. And yeah, so now we're saying, I know this is a little confusing, but you'll get this. We're saying, all right, when we reach the bottom of all of the courses, I want to display this section. So while the data is being loaded, we can say loading more,
So while the data is being loaded, we can say loading more, but after it has been loaded, we will refresh the courses and the pagination. And we are once again, overriding the query string to specify which page we want to load. And here's the cool thing. If you're thinking, well, what about when you reach item 100
If you're thinking, well, what about when you reach item 100 and current page plus one ends up being a page that we don't have results for? It'll all just work because at that point, we can set it up to never make another request. And that's where we can work with the always prop, but one thing at a time. All right, so cross your fingers.
but one thing at a time. All right, so cross your fingers. We will open up the network tab. We scroll down to item number 10. We make a new request to page two. It's working. If we do it again, we can make another request to page three. This is infinite search. So keep coming
This is infinite search. So keep coming and we're just getting all of that data, but we are fetching them in increments of 10 just for the example. You might want to increase that. Cool, but yeah, let's keep going. If I were to continue this to the point where we get to number 100, yeah, notice it tried to fetch a page that doesn't exist.
to the point where we get to number 100, yeah, notice it tried to fetch a page that doesn't exist. Let's scroll up, page 12, page 13. It just keeps trying to fetch the next page, but there is no data here. All right, so here's what we can do. Right now, we're saying always, where are you, where are you? Yeah, right here. We're saying always rerun that query
Stopping at Last Page19:04
Yeah, right here. We're saying always rerun that query when the section is visible. But now I'm going to bind this. I'm going to say, no, not always, but most of the time. I want to do this always as long as we have not reached the end, right? So how might we represent that? Well, I did say not reach the end, right?
So how might we represent that? Well, I did say not reach the end, right? So whenever I can, I like to just make it crystal clear. I'll just say always if you have not reached the end. All right, next, let's clean this up really quick. Just like that. And why don't we put always at the top? Yeah, maybe something like that. All right, so let's create a computed prop. Let reachedEnd equals a computed property.
All right, so let's create a computed prop. Let reachedEnd equals a computed property. And what is the logic that determines whether or not we have reached the end? Let's see if AI can figure it out. So we're going to check to see if the currentPage, and already this is not quite right, because we would have to say props.pagination.currentPage is greater than or equal to the lastPage. Is that right?
is greater than or equal to the last page. Is that right? Well, let's have a look. Let's open up the view tab. To be honest, I can never quite remember what the name of the pagination props are. So let's see, we have currentPage, lastPage. So yeah, as long as the currentPage is less than or equal to the lastPage, then it's good, we can load more.
is less than or equal to the last page, then it's good, we can load more. Yeah, that seems to make sense. Are we all on the same page? As long as we have not reached the end, we should always refresh this section. But at the point we do reach the last page, always returns false, which means, nope, we're done. We're not going to fetch it any more times. All right, let's see if we got it right.
We're not going to fetch it any more times. All right, let's see if we got it right. Let's go to the top and start from scratch. Open up the network tab, and let's begin. So we have page three, page four, and I want you to focus right here at the bottom. Page five, page six, page seven, page H, page H, page eight, nine, 10, and you can see it doesn't continue. Even if I scroll back up and I go down,
and you can see it doesn't continue. Even if I scroll back up and I go down, it doesn't fetch any more because reachToEnd() now returns true, which means always is bound to false at this point. And yeah, this is a perfectly fine way to deal with this. And you can configure it a little more to fit with your needs, but this would be just fine. All right, and that's basically it. So here's the final thing I want you to keep in mind.
All right, and that's basically it. So here's the final thing I want you to keep in mind. What we've done here is great. It'll do the trick just fine, but I don't want you to forget about that GitHub issue I linked to. Assuming they patch it up, and I don't have any reason to imagine they won't, as soon as they do, this becomes a little bit unnecessary. At that point, you could once again return
as soon as they do, this becomes a little bit unnecessary. At that point, you could once again return to something just like this. Inline this, paginate the results, and the deep merging strategy will work at the point where they tag a release to support it. But yeah, until they do, you need to make sure that you split this object up into arrays so that they can be properly merged in. As of the current release of Inertia.
into arrays so that they can be properly merged in. As of the current release of Inertia. And actually, let's finish up by checking which version I'm using right now. If I go to my package.json file, Inertia, I'm using 2.0, but you know what? I think I'm actually using 2.0.3. Yeah, even if we were to update this to 2.0.3, we're still in the same boat. So just keep that in mind.
we're still in the same boat. So just keep that in mind. But either way, with either implementation, it's still laughably easy. So tell me what you think. Bye.
