Introducing Drag Sorting0:04
All right, here's another little Diddy for you in livewire four, uh, one of my favorites and it was a last minute edition and that is built in drag sorting in livewire four. So we're gonna take these metrics here and make 'em so that a user can just drag 'em around to reorder them to their preferences. And I'm not sure I've seen a simpler way to have drag sorting in an app, so I'm excited. But let's go. Alright,
Refactoring Metrics List0:25
to have drag sorting in an app, so I'm excited. But let's go. Alright, so the first thing is these metrics here, I refactored them to be one computed property called metrics that returns a list of them. So there's views, visitors time, and then these each have like what their label is, what their trend changes, and what the actual data from the database is. And then down here in that island for the metrics,
and what the actual data from the database is. And then down here in that island for the metrics, I'm just looping through those metrics. And then I'm rendering a blade component dynamically with a key for the name and then the heading number and the change right from the metric. So this is very similar to having like a collection of eloquent or yeah, eloquent models. Like if you had posts, maybe you have a table and you're looping through posts
Like if you had posts, maybe you have a table and you're looping through posts and you would've a loop like this. And each row would be a component and you want to add drag sorting to it. So all these things apply to sorting in tables, sorting in lists, even as far as like Trello, clone Kanban, board stuff. That's a little bit out of the scope of this tutorial. But everything is the same concept that you just build on.
Enabling Wire Sort1:23
That's a little bit out of the scope of this tutorial. But everything is the same concept that you just build on. So let's dig in. It all starts with a single attribute wire, colon sort. That's it. You take the container of elements. So in this case, this is a container right here and we say we want to sort the items inside of it. And that's it for getting just the sorting functionality working.
And that's it for getting just the sorting functionality working. Like check this out. It gives you all of that for free. You can click on something, you can drag it around. Ah, it's just so beautiful and so powerful right now. One thing I noticed actually right now, I didn't really prep for this, but this button is also sortable and it's not even gonna be sortable correctly because it's absolutely positioned.
and it's not even gonna be sortable correctly because it's absolutely positioned. So if you have an element in your list that you want to make exempt from sorting, you say wire sort, ignore or dot ignore, I'm actually guessing here. So there we go. Wire sort, colon ignore, and it is now ignored. You can't drag it. It's not a part of the sortable list. So that's one of the nice little features in here. And I guess while we're at it, let's just look at
So that's one of the nice little features in here. And I guess while we're at it, let's just look at what drag handles would look like. So if I go inside analytics metrics, this blade file here, this is that blade component. Let's say we only want the heading to be a drag handle. You can say wire sword handle. I believe I didn't prep for this either. Uh, so now I can't drag anything else inside here, but I can drag the heading.
Uh, so now I can't drag anything else inside here, but I can drag the heading. So you could make a little icon that's like a hamburger kind of drag handle and make that the drag handle. So there's a lot there. So let's back out of that but not kill our whole component. Okay, so you got sort, you got handle and you got ignore. And now the front end portion is pretty much taken care of for you automatically. Now of course you want this to be backend driven.
of for you automatically. Now of course you want this to be backend driven. You want it so that when you reorder, you're storing those new positions in the database or something or whatever, so that theyre, they reorder it and then they refresh the page and it's still reordered. I'll show you how it's just not working right now. So if I make visitors the first item here and then I refresh, you'll see that visitors is back to being the second item and that's a problem.
Handling Sort Backend3:33
and then I refresh, you'll see that visitors is back to being the second item and that's a problem. So how do we handle this? Well, wire sort has, you can, it's basically like wire click. You can pass an action to it, like handle sort. Okay, so let's add this up here. So we're gonna go up here next to our computed property called metrics and we're gonna add public function handle sort.
to our computed property called metrics and we're gonna add public function handle sort. Okay? And then this accepts two parameters. The first one is the sort item, the second one is the new position of that reordered item. And we'll dd these out just so you can see here. So item and position. And now when I drag visitors to the front, okay, so the item is null, we'll get to that in a second. And then the new position is zero. Okay?
so the item is null, we'll get to that in a second. And then the new position is zero. Okay? If I drag it back, you're gonna see the new position is one. So positions are indexed by zero, just like an array. So this is position zero, one and two. And when you reorder it, it's just calling that method and giving you the item that you ordered and the new position. You might think, how come it's not giving me an array of all the positions and all the items?
You might think, how come it's not giving me an array of all the positions and all the items? And that's what I wanted too when I started building this. But I realized this is a much better pattern because often you're persisting positions in a database and it's just much better if you're surgically taking the one item and its new position and doing the backend work to reorder everything else instead of relying on the front end
to reorder everything else instead of relying on the front end to give you the perfect order of things. This also allows you to have sort groups or different sorts on the page where you can sort, you can drag an item from one group into another group. You would then need to have two arrays of both groups where this is like you just know the item that moved and its new position and you can reason about everything you need to on the backend given those two pieces of
and its new position and you can reason about everything you need to on the backend given those two pieces of canonical information. So this really is the best pattern, you're just gonna have to trust me on it. So item is null right now and that's because we don't have a wire colon item. So we need that. Our wire sort colon items. So let's do this. So we already have our key 'cause it's in a loop.
So let's do this. So we already have our key 'cause it's in a loop. We're gonna add sort item and we're gonna set that to the name as well. So now when I drag visitors over, it's gonna say visitors 'cause that's the key is now position zero. Perfect. So that's really all the information we need in the markup over here. And now we can build up our handle sort and a bunch of other things to make this all happen.
And now we can build up our handle sort and a bunch of other things to make this all happen. So we need a place to store. I mean typically you'd probably have, this would be like a database table and maybe you'd have a column called position and you would do that work in the database. But here to keep things simple, I'm just gonna do it in a public property. So I'm gonna add a public property
I'm just gonna do it in a public property. So I'm gonna add a public property and I'm gonna call it, let's say sorted metrics. Okay? And then this is gonna be array, an array of just the names. So view visitors and I think time is the other one. Okay? So this is a simple array just of three items and the order of this array is what we're gonna change when we get the new item in position.
what we're gonna change when we get the new item in position. We're gonna mutate that order. But before we do that, let's rig this up so that down here, well let's not do that 'cause I wanna, I wanna show you at break first and then you'll see why we need to do that. Alright? So in handle sort, now I have, I wrote a little bit of PHP, it's very simple. Uh, my, my snippet has the wrong thing sorted metrics. Okay?
of PHP, it's very simple. Uh, my, my snippet has the wrong thing sorted metrics. Okay? So this is how we would re-sort an array based on a new item in its new position. First we take the array and we remove that item from the array. So I'm basically, if it's visitors, we're gonna pluck it out, right like that and then it's just gonna be view in time.
we're gonna pluck it out, right like that and then it's just gonna be view in time. Then we're gonna re-index the array with array values so that instead of it being index zero and then index two without that middle one, it's gonna be index zero and index one. Then we can reinsert the item back in the position that was passed in with array splice. So it's a little bit of PHP. Again, you're gonna have to do this somehow if it's,
So it's a little bit of PHP. Again, you're gonna have to do this somehow if it's, if you're working with an array, it's up to you. Then you would do this in PHP. If you're working with an eloquent model, you'll have to do a little bit more complicated work to reorder or to change a sorting uh, column in a table or a position column in a table and then reposition all the other items. But that's again outside of the scope of this, outside
and then reposition all the other items. But that's again outside of the scope of this, outside of the scope of this feature, we're just giving you a nice sorting affordance that gives you the item in position. You have to handle all of the backend work yourself because everybody's needs are just so incredibly different. But this is gonna do for us for now. Okay, so now we have this property, now we have this stuff and if we do this sort now let's open up the dev tools and see that this request is actually fired.
and if we do this sort now let's open up the dev tools and see that this request is actually fired. So I drag it over and okay, a request was fired but you might have noticed something. Notice how we have views and visitors and I drag visitors over and all of a sudden, oops, visitors is back in the second place. Why did this happen? This happened because when we're rendering the metrics,
Why did this happen? This happened because when we're rendering the metrics, we're not honoring the ordered metrics property that we made that stores the actual order of them. We're just re rendering them in the hard coded order inside the computed property. And this is just how it works. It makes sense that when you drag something over live wire is going to fire request to store the new position and re-render everything.
Rendering Sorted Order8:40
is going to fire request to store the new position and re-render everything. And if you didn't do the work of re rendering things to honor the new sort order, it's just gonna go bloop. We're gonna undo it, right? So we don't want that. So we can go down to our markup down here and instead of looping through that computed property, we're gonna now loop through sorted metrics. And this is only giving us the name, it gives us the index and the name.
And this is only giving us the name, it gives us the index and the name. So I'm just gonna get the name out of this and so name is still valuable, but here we need to just tweak this a little bit. We need to now say this metrics and then access it as an array with the name and then we get heading number and change. So I don't know if you followed that. It's very, very simple.
So I don't know if you followed that. It's very, very simple. We refresh everything should look exactly the same un undefined array key view. So this arrow, should we just debug it right here? Uh, is it's probably called views I think. Yeah. So if we look up here, it's views, visitors and time and I named this view so we're getting an error and that makes sense. Okay, so now views, visitors time,
so we're getting an error and that makes sense. Okay, so now views, visitors time, and now if I drag visitors over it does that refresh but it preserves the sorting. Beautiful. So we're pretty much almost there. It's just finishing touches. One of the finishing touches I want to add is because we dragged it over and because it fired a normal live wire action, like that handle sort thing, it re rendered the island.
because it fired a normal live wire action, like that handle sort thing, it re rendered the island. In this case it re rendered it so it's fine, but it might be a little weird to the user is like I dragged it and then they all update it after a little bit and that's a little weird. And that happened because it's a normal live way or action. It's gonna re-render the island or the component or whatever. So a little touch that you can do is on handle sort,
Optimistic Updates with Async10:19
or the component or whatever. So a little touch that you can do is on handle sort, you could say render list and render list is super useful for things like this. Things that you just say, hey, the ui, the thing the user is seeing in their browser is actually in a good state because this is called an optimistic update. We updated the UI in the client first and now when we re-render the page, we're not actually giving them any new information,
and now when we re-render the page, we're not actually giving them any new information, we're just hoping that we mirror the current information that's already there. But in this case we get that little bit of weirdness because all of the metrics refresh. So we added render list and now you'll see that I'm dragging things around. The requests are getting sent, but there is no problem here as far
The requests are getting sent, but there is no problem here as far as these changing. It just feels very like, oh I'm just dragging it around. The user doesn't see the dev tools, they just go, oh this is really fast and really nice. The other thing that's worth adding for something like handle sort is async. So we're in an island so I think these requests are already asynchronous but maybe not for that island.
So we're in an island so I think these requests are already asynchronous but maybe not for that island. But anyway, you're gonna add Async because you might end up in a situation where you have a really slow connection and you drag one over and the request is out to the server, then a user drags another one back and now there's like requests coming back and waiting for other requests. You add async and now it's like set it and forget it.
and waiting for other requests. You add async and now it's like set it and forget it. Throw this information to the server, let 'em handle it however they want. And just fire requests in parallel. Uh, I'll try to simulate this. So let's take this and let's make the network really slow. So I'll make it like 3G slow. And now if I drag these around, you'll see that all these requests get sent in parallel
And now if I drag these around, you'll see that all these requests get sent in parallel and then they all come back whenever they want to. We're not rendering anything, we made it async. So this is beautiful for optimistic interfaces where a user just interacts with the page, everything feels instant, they don't know anything else, it doesn't block the rest of the page. It's not spinning, loading indicators for other things. Really, really handy. So render list and async are huge.
It's not spinning, loading indicators for other things. Really, really handy. So render list and async are huge. You can use 'em for all sorts of things. Async is new in Livewire. Four render list has existed since Livewire three I believe. So that's pretty much it, but there's one final little cherry on top right now. A user goes, I want average time on pass to be the first one. Then they refresh their page
Persisting Order in Session12:31
pass to be the first one. Then they refresh their page and they go, Hey wait, I already reordered it. That was my preference. How do you save that preference? Well again, typically you're saving this preference in the database. So it's already kind of built in for you. But in this case we're hard coding it as a property up here. Well, another feature that's been in Livewire since Livewire three is called session.
Well, another feature that's been in Livewire since Livewire three is called session. So you can really easily just say, Hey, this property, whenever it's changed, I want you to store that change in the user session in the backend. And now every time that component is loaded, I want you to get that value from the session. This is awesome for keeping multiple components in an app completely in sync or a preference like this
completely in sync or a preference like this that you maybe don't need to store in a database. You probably should. But this is just a, for the demo basically is like they can put average time on past or on post right there and then they refresh and then it's still there. So it's so easy. We have beautiful drag sorting, doesn't block the page at all. Super fast, super interactive, super nice drag handles, uh,
doesn't block the page at all. Super fast, super interactive, super nice drag handles, uh, ignoring other elements. And then all of those preferences are persisted because of a single line of code right up here, which I think is super elegant. So that's wire sort 1 0 1. You can nest sorts, you can have like a big sorted thing like for a Kanban board, like you can sort the columns
a big sorted thing like for a Kanban board, like you can sort the columns but also sort the items inside of it. You can have group sorts so that you can drag things from one group to another. That honestly is an entire series that I may record on how to do sorting to the max 'cause there's so many different ways to mix and match it and it's incredibly powerful. So, uh, I hope you dug it as much as I do.
and it's incredibly powerful. So, uh, I hope you dug it as much as I do.
