00:02
The last new feature in Inertia 2.0 that we're gonna cover is merging props. It allows us to specify preliminary data
00:10
that we wanna send with that initial render of our page, and then we can fetch new data afterward to merge into that preliminary data, allowing us to create kind of an infinite load style
00:20
behavior inside of our page itself. So for example, on our courses index page here, I've gone through and just created a bunch of courses. We have 20 total here.
00:29
Just giving us some scroll room to work with here with this behavior. Then inside of our courses index controller method,
00:36
what we wanna do is first switch this courses variable to instead of being a course array, instead be a model paginator contract of type course,
00:45
allowing us to specify that we want page one of our courses to be our course one through 10, and then page two is course 11 through 20.
00:55
Or we could do it in increments of five as well so that we can see and test things out with more than one additional page of data if we wish. So our first objective is to switch our get courses actions handle method
01:05
from returning back a promise of course array to instead returning us back the model query builder as a whole so that we can build off of it with additional query statements. So if we dive into this action,
01:15
all we need to do in order to do this is get rid of the async keyword off of this action. The query builder itself isn't executed until it's actually awaited.
01:24
So by getting rid of the promise from this type, we no longer need to directly await it for the handle and can instead build additional statements off of it.
01:33
So for example, if we do dot, we can see that we have access to all of the model query builder methods, including the one that we're after here, paginate. We wanna set the initial page that we're after as one,
01:43
and then per page we can load in, let's do five courses so that we have four pages worth of data. By adding paginate onto this,
01:50
this changes our courses variable type from a course array to now a model paginator contract of type course, which is exactly what we were after. Then we need to inform Inertia
02:00
that we want this courses prop to rather than overwrite itself with fresh data whenever we make a new request out, to instead merge the original data with that new data.
02:10
And the way that we do that is via a new inertia dot merge method. And just like defer this to accepts in a callback method,
02:20
that should then return back that data to be merged into our courses. In our case, that's going to be our courses array.
02:27
Now this courses variable is now a model paginator contract. When this type is serialized, it will actually put our courses on a data property,
02:35
and then it will put the metadata on a meta property on the courses variable itself. Prior to serialization, the model query builder contract
02:43
actually still behaves like an array-like structure. So for example, if we do courses dot, you'll see that we still have our array-like methods.
02:51
So we have fill, filter, find, every, all, all that fun stuff, but we have additional meta-based properties accessible as well, like getting the current page,
03:00
setting the base page for the pagination links, first page, and then there's also get meta, which allows us to just reach for and get the metadata from the paginator.
03:10
We will use that here momentarily, but first we can use the array-like structure of the paginator itself to easily convert our courses into an array by doing array dot.
03:19
Let's give ourselves a little bit more room here to work with from to convert our courses back into an array. Then so that we know what page we're currently on on the front end,
03:29
we also want to return back the metadata from that paginator as well, which we can pass in as a separate prop called courses meta. And this is where we'll want to reach for that
03:38
courses dot get meta method that we just saw a moment ago. All right, so lastly, inside of our controller, we need to make the page that we're on actually dynamic. So what we can do there is jump back
03:48
into our courses validator file and create a new validation. So we'll export const course paginate validator equals vine dot compile,
03:57
pass in a vine object into there with a page property of type vine number. Pages start at one, so we can ensure that this is positive
04:06
by adding positive onto our validation. And then we can fall back to one if it's not provided. So we'll make it optional as well. Then we'll have our per page of type vine number.
04:15
Rather than specifying this one as positive, we can instead limit it to a range of say, between five and 50, ensuring that the users aren't going to try
04:24
and request too many in any one query. Then we'll make this optional as well and specify a default back inside of our controller. So first, what we want to do is grab our request
04:34
out of our HTTP context, const, grab our page and per page from await request validate using,
04:42
and then pass our course paginate validator into there. For our paginate method then, we'll first try to use the page. And if it doesn't have a value, we'll default back to one.
04:52
Then we'll do the same thing here with our per page, defaulting back to five if a page is not specified. Now, instead of a real application, you would probably want more than five items per page,
05:01
but we're doing this so that we have more pages to work with with our 20 courses on our page itself. So that's why we're using five here at present. And that also does it now for our controller.
05:11
So we can now jump into our courses index page. We're still providing a course DTO array for our courses prop,
05:18
but we also now want our courses meta data as well. And the type for this, if we quickly jump back into our courses controller, this method's currently returning back any.
05:28
However, the Atacaz DTO package that we're working with to convert things into a DTO has a simple paginator DTO meta contract
05:37
that we can work with that contains a one-to-one mapping of the model paginators types in the DTO fashion. Then we can scroll on down to our table
05:46
where we are looping over our courses inside of it for our rows. Let's go ahead and collapse that row down and just underneath it, let's add in Inertia's when visible component.
05:56
Inside of here, we can specify just the fallback slot. And inside of this fallback, we can specify a table row that contains a table cell
06:05
that spans all five columns that we have inside of the table. You can pretty this up a little bit more, but I'm just going to put loading dot, dot, dot to signal that we are loading some data.
06:15
Now on this when visible component, we don't want to add in data like we did in the past with our deferred information because we already have data loaded on our page.
06:24
So this would just never show if that were the case as it would just be checking to see whether or not that prop has a value. Instead, we want to use the reload functionality
06:31
of when visible, hook into that and specify the new page. And to do that, we'll add in a params prop, make that dynamic.
06:39
And this accepts in reload options that are relatively similar to visit options. So we can scroll back up to our script and let's add in a new computed property.
06:48
So we'll do const when visible params equals computed from view, provide in our callback function and return back a new object.
06:56
The when visible component is going to do a reload for the current page. So we don't need to specify a URL for the request here, but we do want to specify additional data that it should send.
07:06
And that additional data is going to be the page that it should request from our paginator. We can grab the current page via props forces meta dot current page.
07:15
And then for the request, we want the next page. So we'll increment that by one. Since our reload request is going to be a get, this data will be appended on as a query string
07:24
and our validator will pick that up accordingly. We also don't want that query string appended onto the user's browser URL, because if they were to then refresh the page,
07:32
they'd have no way to go back to the previous page. So we don't want to lock them there. So we can omit that from the URL by specifying preserve URL to true, and that will just prevent this request
07:42
from updating the user's URL. Now inside of our controller, we only have our courses and courses metadata being queried and loaded in. But if we had additional data,
07:51
we could also add only in here to specify that we just want to load in the courses and courses metadata. And that works the exact same as every other link usage inside of Inertia.
08:01
So for example, if we had additional data being queried inside of our controller, we'd want to ensure that that's being queried inside of a callback function within our props so that Inertia has a way to omit that data
08:11
whenever it queries just for our courses and courses metadata. We don't need it, but I'm going to go ahead and leave this here for reference. Then we can scroll back down to our when visible component, provide in our when visible params.
08:21
And the next thing that we need to do is have a way to turn on or off the intersection observer inside of the when visible component. By default, the when visible component
08:29
will turn itself off after it sends out the first request. We don't want that to happen. We want this to send a request until we've loaded all pages for the paginated data that we have to load.
08:39
So we can use the always prop that we learned about in the previous lesson to programmatically be able to turn this on or off. So we'll scroll back up to our script, give ourselves a couple of lines here,
08:49
and let's do const as more pages. Set this equal to a new computed property. And inside of here, we want to grab the current page
08:58
as well as the last page from our courses metadata. We want to return true if the current page is less than the last page.
09:08
So if the current page that we're on is less than the last page, we want our when visible component to send requests out whenever it becomes visible inside of the user's browser.
09:17
If our current page is equal to or somehow greater than our last page, we can turn that when visible component off so that it doesn't send new requests out.
09:26
So we'll scroll back down to when visible, pass our has more pages into the always prop, and that should do it. So we can give this a save. Let's go ahead and jump back into our browser here. Looks like it needs a refresh, so we'll go ahead and refresh here.
09:36
At present, we see courses one through three, but up through five are loaded, and we don't have any additional requests right now that have gone out for new page information.
09:46
If we go ahead and start to scroll, we get down to five. We see that courses for page two goes out. Since we've preserved the URL, that query string has stayed out
09:55
of our URL bar for the user. And if we take a look at the response data for this request, we'll see that the props that we get back include courses six through 10,
10:04
and then we also have new course metadata as well. And then lastly, we have merge props down here specifying that courses should merge preexisting data with the new data from this request.
10:14
Since courses meta is not a merge prop, this new data that we've gotten here is not merged into the preexisting data, but instead overwriting that preexisting data.
10:24
So our courses meta on our page now reflects this new data one to one, whereas the merged courses were appended
10:32
onto the preexisting courses we already had loaded in. We can go ahead and hide this response away and continue scrolling. Once we reach 10, we should see page three go out.
10:41
Once we reach page 15, we should see page four go out. And then once we reach test 20, we should see no additional requests go out because our Hasmore pages has turned
10:51
the When Visible intersection observer off for us. Then just like as we were covering with our defer, whenever that request is going out, our fallback slot is rendered for the user.
11:01
So they'll see the loading indicator here, and you can always pretty that up to your liking. Once that new data is loaded in, it will go away and instead show the new data.