تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Entering Search Mode0:00

What else does a data table have? Well, we would love to be able to search our data table, right? So let's add some functionality to search our data table. Back in our browse state, let's add another keypress listener. And I think when you press /, you should go into search mode. We'll say on / this search. And I'm going to extract this out to a method because we're going to be going into another state just the way that we're in this browse state. We're going to go into a search state. So let's handle that search method.

We're going to go into a search state. So let's handle that search method. protected, function, search. And we're going to set the state equal to search. And we're going to reset the index. If we're in search mode, let's just reset the index to zero. And we're going to reset the page equal to one so that we sort of reset. As soon as we go into search, we reset the entire mechanism of the table. And let's grab this here because we're going to be doing the same thing. We're going to clear our existing listeners.

And let's grab this here because we're going to be doing the same thing. We're going to clear our existing listeners. And we're going to listen for a quit still. And now we're going to use a helper method on the Chewy keypress listener. That's called listenToInput. And let's see, we have a value and a cursorPosition. So when we're typing into the terminal in our Chewy, we need to track the cursorPosition and the value ourselves. So the value of what is being typed and the cursorPosition needs to be tracked by us.

So the value of what is being typed and the cursor position needs to be tracked by us. So let's set up a couple of properties here. We're going to have a public string query. And this is going to be our search query. And then we're going to have the public int cursorPosition. We're going to default this to zero. So the cursor is going to start at position zero. And we can pass these right along to listen to input. And this will handle our key presses and our arrow.

And we can pass these right along to listen to input. And this will handle our key presses and our arrow keys for a cursor position automatically. And we can take a look in here and see what is going on. And we are going to say listen. So listen to input. OK, so we're basically setting up a lot of internal listening and adjusting the cursor position and adjusting the value based on the various keys that are pressed. So this is a lot of things abstracted away.

on the various keys that are pressed. So this is a lot of things abstracted away by Chewy that allow us to easily listen to input based on the query and the cursor position. Finally, when we press the Enter button, we should go back into browse mode if we have any results. So we can search. We press Enter, and then we go back into browse mode and browse the results of our search. We can say on key, and this comes from prompts, Enter.

and browse the results of our search. We can say on key, and this comes from prompts, Enter. We are going to say if we have any results, if count this visible, and we'll have to adjust visible to account for the search query. We'll do that momentarily, equals 0. We're just going to return. We're not going to do anything. We're going to stay in search mode because there's no results right now.

Filtering Visible Rows3:02

We're going to stay in search mode because there's no results right now. Otherwise, let's go back into browse mode. Let's adjust visible to handle our search query so that we filter records based on the current search query. So let's dive into visible. And right now, we're just returning a slice of the rows every time. So we actually need to check and see if we have a non-empty string query and handle that.

So we actually need to check and see if we have a non-empty string query and handle that. So if this query equals an empty string, we're going to return what we've been returning, this array_slice. Otherwise, we need to filter our rows based on the query. filtered equals array_filter(this rows). And then we're going to map over the rows. And we are going to check if the string contains. We're going to mb_string to_lower(implode(the row)). So we're going to just get it as one single string.

We're going to mb_strtolower implode the row. So we're going to just get it as one single string. And we're going to say mb_strtolower this query. And these should be our filtered records. So now we're going to say, OK, we need to reset the total pages to be equal to this getTotalPages of our filtered rows. And then we're going to say the results are equal to array_slice our filtered rows 0 $perPage. If count $results, and it should be $results, is greater than 0, we can go ahead and just return our $results.

If count results, and it should be results, is greater than 0, we can go ahead and just return our results. Otherwise, let's return an empty record set so that we know when we don't have any records. So we'll say name is actually just going to be no results. Email is going to be an empty string. And address is going to be an empty string as well. So this will just be our little fake empty result set for when we have no results here. So if we have an empty query, we just return our default behavior.

set for when we have no results here. So if we have an empty query, we just return our default behavior. Otherwise, if we have a query, filter the records down based on finding that query in the row, reset the totalPages, and then return the sliced results. One more thing we have to do, we have to reset the totalPages up here as well so that as we bounce back and forth between having a query and not having a query, our totalPages do not get out of sync. So we will say this totalPages rows. Now let's hop back to our renderer and actually render out

Rendering Search UI5:34

So we will say this total pages rows. Now let's hop back to our renderer and actually render out the search functionality. So at the top of our renderer, we're going to say this, render, search. And we're going to pass the prompt. So we're going to extract this out to a method. And we're going to say protected function render, search. And we are accepting a dataTable prompt. We are returning void. And we're going to say, OK, if the prompt state is equal to search,

We are returning void. And we're going to say, OK, if the prompt state is equal to search, well, we're in search mode. Let's render out a line, search, and then we'll say prompt. And we're going to need to get the value of the current search with the cursor. And prompts actually helps us out with that. So I'm going to write this out, and then we'll implement this method afterward. So prompt, value, width, cursor.

implement this method afterward. So prompt, value, width, cursor. And we'll say a width of 60 max. So it'll truncate it after 60. We'll implement that momentarily. We can just return after that. That's all we need. If our promptQuery is equal to an empty string, we can just return. We don't need to deal with the search. Now, if the promptQuery is not an empty string,

We don't need to deal with the search. Now, if the prompt query is not an empty string, and our state is not search, well, we're in filtered record mode. And we want to display the fact that we are to the user. So we'll say this line, this dim. And we'll dim it so that they know they're in search mode, but they're actually in browse mode. They're browsing filtered records. We'll say search. And we'll just write out the query here.

We'll say search. And we'll just write out the query here. Prompt query. OK, let's figure out this valueWidthCursor method. Back in our Application class, we are going to import a trait from Prompts. We're going to say use TypedValue. And this helps us deal with typed values and cursor position more easily. So let's head down, and we'll add a method here called public function valueWidthCursor.

So let's head down, and we'll add a method here called public function valueWidthCursor. And this accepts the maxWidth. This is something that the prompt's internal helper needs. We're going to actually extract this out to another method, because we're going to be using this in the future. This getValueWidthCursor. And we'll say this->query, we're going to pass it the query, and we'll pass it the maxWidth as well. function getValueWidthCursor.

and we'll pass it the max width as well. Function getValueWidth cursor. And it accepts a string value and int maxWidth and returns a string. And this is where the actual typed value trait comes into play. We're going to say if the value is equal to a blank string, we're going to return this dim, this addCursor, which is a helper in that trait. And we're going to say a blank string, zero, maxWidth. And if it's not, we're going to say return this addCursor value, this cursorPosition, and the maxWidth.

And if it's not, we're going to say return this addCursor value, this cursor position, and the maxWidth. And so this automatically will add a cursor to the value in the correct position based on the cursor position. This is something that comes with prompts. It's super useful. Let's clarify this method, because it seems a little bit funny with this. Instead of value, we'll say queryValue width cursor. And over here, we'll switch this to queryValue width cursor. That was a lot.

And over here, we'll switch this to query value width cursor. That was a lot. Let's see how this came out. We ran it. And we earlier defined the property cursorPosition. And actually, that's already defined in the TypedValue trait. So we can actually just remove that from our Application class. We can just remove this. So now let's run it again. And so far, so good.

So now let's run it again. And so far, so good. We are going up and down. We're able to paginate still. And if we press our slash key, we go into search mode. And let's see. We can search for Fahy. Yep, we found it. We search for knee. OK.

We search for knee. OK. And so if we press Enter, it should dim. And now we're back in browse mode. And if we press slash again, we're back in search mode. So we can toggle between these two things. And we can clear this out and press Enter. And now we're no longer in search mode. So this is looking really good. One thing I don't really love is that it's jumping back and forth.

So this is looking really good. One thing I don't really love is that it's jumping back and forth. As you press search, that table kind of drops down. It would be nice if everything stayed in place. I like it when it doesn't shift the layout. So let's fix that real quick. We can easily fix this in our renderer by just checking the output property. So as we render these lines out, as we render basically, like if we say this line, what we're actually doing is we're appending to an output property on the renderer.

like if we say this line, what we're actually doing is we're appending to an output property on the renderer. So we can say, if this output is equal to nothing yet because we haven't rendered a search and no search has been rendered, why don't we just put a placeholder line there? So we basically are saying, OK, create a placeholder since we don't have a search so that when we do have a search, that line can go away and get replaced by the search. And let's just check that out real quick. Great.

And let's just check that out real quick. Great. So we're here. If we press search, oh, it's so much better. I like that so much better. The layout shift just drives me crazy. It's so nice to have that nice and clean. OK, what else does a data table do? Well, it would be nice to jump to a specific page if we were interested in doing that.

Implementing Page Jump11:04

Well, it would be nice to jump to a specific page if we were interested in doing that. And so we can pretty easily do that by sort of replicating the search experience with a couple of modifications. Let's do it. OK, first up, we can just basically replicate this and say jump to page. And so this is going to be our value for when they're typing which page they want to jump to. Next, let's say on browse. Let's add a new key press listener.

Next, let's say on browse. Let's add a new key press listener. And we'll add J. And we'll say this jump. And we can grab a lot of search and just replicate that down and call this jump. And we will set the state to jump. And we'll set the index to 0. And we're not going to worry about the page number necessarily. So we still want to clear existing. We still want to listen for quit. And we want to listen to input.

We still want to listen for quit. And we want to listen to input. This time, instead of query, we're going to listen to jump to page. And the cursor position is still the same. And on enter, well, what do we want to do? Let's clear this out. And we'll say, OK, if this jump to page is equal to a blank string when they enter, we're just going to go back into browse mode. And we're going to bail. OK, what's next?

And we're going to bail. OK, what's next? We want to make sure that the page they're jumping to, the thing that they have typed in, is numeric. If it's not, we're just going to ignore it. If is_numeric, this jumpToPage, return. If it's not numeric, get out of here. We're not interested. Also, we want to make sure it's within the bounds of the number of pages that we have.

Also, we want to make sure it's within the bounds of the number of pages that we have. So if this jumpToPage is less than one, or this jumpToPage is greater than this totalPages, well, we're also going to ignore that. We're just going to say, no can do. We can't do anything about that. Otherwise, we can actually say this page is equal to the int of this jumpToPage. And we'll reset jumpToPage to a blank string. And we are going to head back to browseMode.

And we'll reset jumpToPage to a blank string. And we are going to head back to browse mode. Great. What else do we have to do? We actually need to set this up as well. So we're going to have a jumpValue with cursor. And we're going to use the jumpToPage to actually track that. That's why we extracted this out to this method, so we didn't have to repeat this logic twice. Great, let's go over to our renderer and see what we can adjust there.

so we didn't have to repeat this logic twice. Great, let's go over to our renderer and see what we can adjust there. So we have renderSearch. We can also say, OK, let's renderJump. And let's scaffold that out. We're going to copy this down, and we'll say renderJump. And we can copy some of this logic. And we can say if it's not equal to jump, well, we can actually just return. We don't need to actually do anything. But if we are in jump mode, we can just say jumpToPage.

We don't need to actually do anything. But if we are in jump mode, we can just say jump to page. And then we can just say prompt jump value with cursor and set it to 60. And that should probably do us. Let's see if that works. OK, so here we are at our table. And if we press J, we get jump to page. Looking pretty good so far. If we set it at 11, not going to do anything. If we just enter in some text, that's not numeric.

This is just a simple explanation with no code.

Displaying Hotkeys Help14:47

This is looking super solid. I'm a big fan of letting the User know what keys they can press and how they can navigate the app. So let's show them some hotkeys. Use draws hotkeys from Chewy. And then we have a couple different states here to handle. So let's actually come down here. And we'll say match promptState. And if it's search, let's show the search hotkeys. And if it's jump, we'll show jump hotkeys.

And if it's search, let's show the search hotkeys. And if it's jump, we'll show jump hotkeys. And otherwise, by default, we'll just show the default hotkeys. And I should probably spell hotkeys correctly here. There it is. Great. So we're handling this. Let's actually implement these methods. I've actually pre-written this out so you don't have to watch me type all of this.

I've actually pre-written this out so you don't have to watch me type all of this. But basically, our search hotkeys are just Enter to submit. Our jump hotkeys are just Enter to submit. Our jump hotkeys are Enter to jump to page. And our default hotkeys are Navigating records, Previous page, Next page, Search, Jump to page, and Quit. And now, there's a couple of things that we need here. So we need to actually, all of these Ks are capital.

And now, there's a couple of things that we need here. So we need to actually, all of these Ks are capital. And we need to actually pass the prompt in here so that it knows about if these particular hotkeys are active. So if it's the first page, we don't want this to be active. But if it's greater than the first page, this hotkey's active. Otherwise, same goes for the next page. We don't want to go to the next page if we're already at the last page. And so this basically will set up, for each state,

if we're already at the last page. And so this basically will set up, for each state, our hotkeys. So let's actually print those out to the screen. So we'll say, collect this hotkeys. And for each of them, we're going to just print them out. So this should show our hotkeys. Let's see if it does. Yep, OK, so we have Navigate Records, Previous. Yep, this is good.

Yep, OK, so we have Navigate Records, Previous. Yep, this is good. And you can see Previous page lights up when we're not on the first page. The same will go for the next page. And if we go to Search, perfect, that works. And if we go to Jump, excellent. So our hotkeys are working. Probably need a little space here. Let's add a new line between our pagination and our hotkeys.

Submitting Selected Row17:05

Probably need a little space here. Let's add a new line between our pagination and our hotkeys. OK, the last thing I want to do is we are selecting rows in this interface. And we should be able to actually retrieve the value of that row back. So we can actually make this a useful interface. So we can say, On, key, Enter. If we're in Browse mode, we can just go ahead and use the submit method of the BasePrompts.

If we're in Browse mode, we can just go ahead and use the submit method of the BasePrompts class to submit this. Submit. Now, we have our value method. And it's just returning null. But that's actually not correct. That wouldn't actually give us back the value that is currently selected. So in order to get that, we can easily grab it.

that is currently selected. So in order to get that, we can easily grab it from the visible rows. This, Visible, this, Index. And that would give us the row, the data row, that we actually selected. So let's dump out the value so that we can see if we're getting the right value back. So LabDataTable. And we'll say the value is equal to $prompt.

So Lab, DataTable. And we'll say the value is equal to the prompt. And we'll just var_dump value. Great, let's check it out. OK, so if we go down to Bow, OK, it says required must not be accessed before initialization. So it looks like prompt is looking for a required property that is not existing right now. Let's see if we can just add it. And maybe that'll solve our problem.

Let's see if we can just add it. And maybe that'll solve our problem. So let's see what required is supposed to be. required is either a bool or a string. So we'll just say that public bool string required is equal to true. It's true that it's required. We have to select a value from the table. So let's see if that works. We go down to Bow again.

So let's see if that works. We go down to Bow again. And we get Bow. Let's go to a different page and make sure it works with our pagination. And looking good. This is a very useful component that we've just built. We have built an interactive data table in the terminal that you can actually select values from. That's amazing.

that you can actually select values from. That's amazing. That's pretty great.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟