Adding Empty State UI0:00
Okay, let's continue working on our to-do app. I want to be able to edit our to-dos here. I want to be able to filter our to-dos here. But first, let's start with an empty state. So when we have no more to-dos in here, let's just show a message and maybe an SVG. So that should be easy enough. We can make use of the conditionals. So again, let's use our snippets here, s-if. The condition is if to-dos.length is greater than zero, then go ahead and show what we already have. So let's grab this end of the if. Let's put it down here somewhere. So it should be somewhere after clearCompleted. Okay. And I believe it goes here. And we also want an else case to show the empty state. So let's add that as well. Say else. Okay. And let's just see if this is the correct spot. So let's say they actually have a class here of noToDosContainer. And this is a noToDos. Okay, let's delete some to-dos here. And that is the correct spot. So let me add another container.
let's say they've actually have a class here of NoToDosContainer. And this is a NoToDos. Okay, let's delete some to-dos here. And that is the correct spot. So let me add another container for the SVG. So let's say NoToDosSVGContainer. And I'm just going to paste in the SVG. I got it from Undraw, where you can find a bunch of free SVGs. It's this one right here. And I'm also going to add a container for our text. So after this, let's say NoToDosTextContainer. And we can say please enter a to-do. Okay. And I'm also going to add some styles just so everything is placed correctly. So let's go back to our style.css. I'm just going to paste them in. Just adding some centering, making sure the SVG is the correct size, and some margin as well. So let's save that. Let's save this as well. And hopefully I did everything correctly. So back here, let's check all. That's clear completed. And that looks good to me. Okay, now let's work on this filter here.
Implementing To-Do Filters1:55
Let's save this as well. And hopefully I did everything correctly. So back here, let's check all. That's clear completed. And that looks good to me. Okay, now let's work on this filter here. So we want to be able to filter our to-dos if they're active or completed. So first, let's work on clicking the button and making sure it's selected. So first, we need a piece of state to hold the current filter. And by default, it's going to be all. So let's do that first. So back up here, let's create a new piece of state called currentFilter. So let currentFilter equals all. Okay. And as we click one of these buttons, we want to update this currentFilter. So let's go to completed. And let's go ahead and add that. So we can add it for all of them. Let's say onClick equals, and I'll just do it inline here. Let's pass in a callback and update that state. So currentFilter equals, and this one is all, this one's going to be active, and this
them. Let's say on click equals, and I'll just do it inline here. Let's pass in a callback and update that state. So currentFilter equals, and this one is all, this one's going to be active, and this one's going to be completed. Okay, let's save that. And you can see I have this class, which shows which one is selected. So filterButtonActive. So let's get rid of that. And now you see all is no longer active. So again, we want to make use of conditional class rendering here. So we can say class: So we want that class. So filterButtonActive if the currentFilter, in this case, it's all. So we can do the same for the others as well. So let's grab this. Let's put it here. But it should be for active. Okay, and same for completed. So after this, add that completed. Okay, save that. You can see the border came back for all. Let's see if it works for active and completed.
Okay, and same for completed. So after this, add that completed. Okay, save that. You can see the border came back for all. Let's see if it works for active and completed. It does. Cool. Now we can make use of a reactive property that changes the toDos that we're showing here. So right now we're just making use of the toDos. So this is all the toDos, which will be the all case. But if we click on active or completed, we want that to be a filtered toDos list. So let's go ahead and do that. So let's add a filtered toDos list. And I'll put that right here. Let's say filteredToDos. And for now, let's just say toDos. And we can loop over these filteredToDos instead of the toDos. So let's grab this. Let's go back to our each here. And let's say filteredToDos instead. So that should be the same. But like I said, depending on what we select here, we want to filter our toDos. So for example,
And let's say filteredToDos instead. So that should be the same. But like I said, depending on what we select here, we want to filter our toDos. So for example, if the current filter is active, then we want to filter our toDos. So we can say toDos.filter(), we only want active toDos. So I'll say toDo toDo not toDo is complete. So that would be for the active case. And if I save that, you'll see it will only show the toDos that are active. And the other case would be toDo is complete. So only show the toDos that are complete. So again, those are our three cases. So let me just go back to this. I'm going to cut this out. And I'm going to make use of a nested ternary here, which isn't the cleanest solution, but it should work for now. So if the current filter is all, then go ahead and just use the toDos. So we'll say toDos here. But if it's not all,
Filtering Displayed List5:21
but it should work for now. So if the current filter is all, then go ahead and just use the toDos. So we'll say toDos here. But if it's not all, then we'll use another ternary. So if the current filter is active, then only show the toDos that are active. So we can just paste in the code that I had. And if not, then it's the completed case, which is just the opposite. So let's remove this not here. And hopefully I did that correctly. Let's save that. All still works. Let's try active, it should be first and the third one. Okay, completed should be the second one. And it is working. Cool. Okay, now let's work on editing toDos. So when I double click the label here, I want a text box to show up, which allows us to edit the toDo. So down here, I already have the code for the text box right here, which is commented out. So if I were to comment that back
Toggling Edit Mode6:09
I want a text box to show up, which allows us to edit the toDo. So down here, I already have the code for the text box right here, which is commented out. So if I were to comment that back in, you'll see the text box beside the label. So if the toDo were in an editing state, then it will only show the input for this would not show the span. So it's only going to be either one or the other case. So that's why I have this isEditing state for each toDo here in our toDos array. When we double click this, we want to update this to true. And when we're in this state, and we blur off of it, then we want to set it back to false. So let's go ahead and do that. So let's put it back to the label here. So let's comment this out again. Actually, let's just go ahead and add the conditional here. So let's go ahead and do add the conditional here. So in this case here, for the span, go ahead and say SF.
Actually, let's just go ahead and add the conditional here. So let's go ahead and do add the conditional here. So in this case here, for the span, go ahead and say SF. Actually, we want the if else case. Okay. And for the label, the condition is if not to do editing is editing. And go ahead and show this span here. So let's put that in there. Okay. And this is the case when we are editing. So let's put that in the else block. Okay. So right now for all to dos is editing is false. So it does show the label. But if I were to change one of them, say for example, the first one. So is editing true, then it should show the text box. Cool. Let me just put that back. So now we want to listen for a double click event on this label here. And then we can update the to do or the editing state for that to do. Let's go ahead and do that.
So now we want to listen for a double click event on this label here. And then we can update the toDo or the editing state for that toDo. Let's go ahead and do that. So back to our label here. Let's add an event listener for double clicking right here. So on dblclick. Let's call a method called editToDo. So we'll pass that in editToDo and we'll pass in the toDo here. So let's go ahead and define that method up here in our script. Let's say editToDo takes in a toDo. And for now, we're just going to set the isEditing property to true. So to do that isEditing is true. So let's save that. Let's give that a try. So if I double click one of them, you'll see that nothing happens. That's because we want to force a rerender on the toDos. So we can just say toDos equals toDos. And hopefully that should work. So let's double click this. And it does go into an editing state. Now let's work.
Committing Edits on Blur8:39
we want to force a rerender on the toDos. So we can just say toDos equals toDos. And hopefully that should work. So let's double click this. And it does go into an editing state. Now let's work on actually updating the toDo. So again, we want to bind the value of this to the toDo title. So we want a data binding on this text input. So let's start with that. So that should be this input right here. And we can say, bind the value to be toDo.title. toDo.title. Okay. So let's try that out. Save that. And as I double click, the correct value should be in here. But we can't leave the editing state. So let's work on that. So we want to leave the editing state when we blur off of this input. So let's say onBlur. Let's call a method named doneEdit. And we'll pass in the toDo. Okay. So let's go ahead and define that method. It's pretty much the same as editToDo. But we are setting isEditing to false.
And we'll pass in the todo. Okay. So let's go ahead and define that method. It's pretty much the same as editTodo. But we are setting isEditing to false. So let's say doneTodo or doneEdit. Let's set editing to false. And let's give that a try. So finish, it's felt screencast. Let's change it to today. Let's blur off of it. And that does update the todo because of our twoWayDataBinding. They should work as well. Blur off of it. And that does work. So again, if I double click it, you'll see that it's not focused by default, I have to click on it again for it to have focus. So I believe you can just add the autoFocus attribute to the input. And that should do it. So let's add autoFocus here. Let's save that. Try that again. And that does seem to work. So I also want to be able to hit enter to commit the change. So right now I can't do that.
Keyboard Commit and Revert10:28
So let's add auto focus here. Let's save that. Try that again. And that does seem to work. So I also want to be able to hit enter to commit the change. So right now I can't do that. If I change it, hit enter, nothing happens. So let's work on that as well. So we also want to listen for keyboard events. So let's go ahead and do that. So we can say onKeyDown. And let's call a method for that as well. This time, we need the event to grab a specific key code. So say event, say doneEditKeyDown, pass in the event, and the todo. Let's go ahead and define that method up here. Let's say doneEditKeyDown, takes in an event, and a todo. And we can listen for the enter key in here. So if event.key equals enter, then we can do pretty much what we did in doneEdit. So let's just call that doneEditTodo. And I also want to listen for the escape key.
So if event.key equals enter, then we can do pretty much what we did in done, edit. So let's just call that done, edit, to do. And I also want to listen for the escape key and do something a bit different. So let's duplicate this. Let's listen for escape. This is fine for now. Let's just make sure enter works. So let's save this. Let's finish Svelte screencast. Today, let's hit enter. And that does work. But for the escape key, I want to be able to back out of my changes. So if I were to say now, and hit escape, I wanted to go back to the text without now here. So we need some sort of state to keep track of that. So let's go ahead and add that. So let's put it right here. Actually, let's put it with all the other edit stuff. So I'll put it right here. Let's name it beforeEditCache. And it's an empty string by default. But right when we edit the to do, we can update that. So
all the other edit stuff. So I'll put it right here. Let's name it beforeEditCache. And it's an empty string by default. But right when we edit the toDo, we can update that. So beforeEditCache equals the toDo title. So toDo title. So now when we hit escape, we can just make use of that cache instead. So right here, for escape, we can say toDo title equals beforeEditCache. And that should back out of our changes. So let's try that again. Let's double click this. Let's change it to today. But if I hit escape, it should back out of those changes. And it does cool. Let's also make sure we can't enter empty strings. So right now we can if I were to delete this hit enter, our toDo is now empty. So we can add a check for that in here and just make use of the cache. So let's say if toDo.title.trim().length is zero. Then go ahead and just use the cached version of toDo.title equals beforeEditCache.
