Project Setup and Template0:32
I'm not going to submit it, but just for playing around. Okay. Here is the template to get started. So I will clone this to my desktop, like so, cd in there, and open this in my editor. All right, and here's what we get. So if you take a look at index.html, you can see they get you started with the CSS and the base markup. You don't have to worry about that. The only thing we do have to worry about is the standard logic. All right.
The only thing we do have to worry about is the standard logic. All right. So why don't we do this? I'm just going to, we don't need php here, but I'm using it as a basic server, and we'll view that. And here's what we currently get, because we haven't yet installed our dependencies. So let's do that real quick, npm install. Run it one more time. And if we give it a refresh, there we go. So now, at this point, none of the logic works.
Initialize Alpine State2:15
There's your footer. So this will have your basic filters and how many tasks we have left. Okay. And then maybe another, a page level footer. Yeah. So let's go over this piece by piece. This section should be hidden by default and shown when there are to-dos. So they're giving you little hints about what to display and what not to display. So with that in mind, here's what we're going to do. Let's go ahead and of course pull in Alpine.
So yeah, at the very least now I could say we'll hide it. And if I give it a refresh, you'll no longer see this. Right? So you only want to see it if there are to-dos. So right off the bat, we can imagine it'll be something like this. Like if we have any toDos. So there's our first data property. And we'll do this piece by piece. So we'll start. Let's do this.
So we'll start. Let's do this. All right. Let's format it like this. Okay. To-dos will be initialized to an empty array. And now if I give it a refresh, I don't see that. Great. All right. Let's keep going.
The list of to-do items. These are here just to show the structure of the list items. Okay. Get rid of that. List items should get the class editing when editing and completed when marked as complete. Okay. So let's have a look real quick. Let's bring it back. So yeah, it looks like this would have a class of completed and that will add the line through. Now if I change that to editing, okay, it swaps it out with the input.
So yeah, it looks like this would have a class of completed and that will add the line through. Now if I change that to editing, okay, it swaps it out with the input. How is it doing that? I guess it's setting the display here. Let's have a look. Yeah, it sets display block, but if it's completed, yeah, they're just using CSS to toggle the display of the edit input as you see here. Okay. That makes sense. Let's keep going.
That makes sense. Let's keep going. So we have two list items here, and again, these are just examples. So like this is how a completed one might look, and if you don't have any class, I guess this is just how a standard item would look. Yeah. Okay. That means I'm going to get rid of one of these. Yeah. So if I click on this, that already works.
And then of course, if we come back and bring this back to what we had before, yeah. Now we're seeing the initial state. Okay. Get rid of that. This should be 0 items left by default. Yeah. So you can imagine, I'm not going to do this yet, but we would probably want something like the activeTasks.length. Something like that is probably what we would add there, but I'm going to leave that blank for a minute.
Add New Todos7:37
We understand what we need to do. So why don't we begin with the most basic functionality? When I type in here and I hit enter, we should add a new task. So if we're thinking in terms of a single source of truth, we know that this array will store all of the to-dos in our application, which means right down here, there's your input, what needs to be done. You'll see it there. So let's listen for when the user hits the enter key. So as we've learned, we can register events like this, key up, but specifically if they hit the enter key, then trigger an action.
So as we've learned, we can register events like this, key up, but specifically if they hit the enter key, then trigger an action. So if I say hi and we type, then I hit return or enter, we get the alert. Okay. So it sounds like we need to push to that to-dos array, or why don't we add a method like addToDo? Okay. There's our first one, addToDo. And again, this would be something like this, to-dos.push a new item here. So we would need to know what is the body, what do we want to call it?
And again, this would be something like this, toDos.push a new item here. So we would need to know what is the body, what do we want to call it? Like the title of the to-do, the description, the body? Maybe body or maybe title, I don't know. Let's stick with body. The body would be whatever the user has typed into here. So if we want to track what the user types into the input, of course, we would use x-model and maybe we'll call it something like newToDo. That's pretty common. So let's say newToDo will be initialized to an empty array.
That's pretty common. So let's say newToDo will be initialized to an empty array. And then when the user hits the enter key, we'll call this method, we will add a new object to the toDos array where the body is the newToDo that you typed in. Next, and of course we'll need to tweak this a little bit, maybe do a little sanitizing, but we'll come back to it because I want to move on to this step. So now if we add our first item to that toDos array, this will reevaluate. We do have toDos, so toDos.length would be one, which means we will render this. So let's give it a shot. Go to the store, enter, and there you go.
So let's give it a shot. Go to the store, enter, and there you go. However, a couple of things. First, buy a unicorn is still hard-coded. And second, as soon as I add a task, we should clear that input for the next task we have. Okay. Let's fix, where does it say unicorn? Yeah, it's all hard-coded here. So it sounds like this list item will be rendered for every to-do in our system. So why don't I just copy this, wrap it within a template, and then we'll say x for to-do
Render Todo List10:02
So it sounds like this list item will be rendered for every to-do in our system. So why don't I just copy this, wrap it within a template, and then we'll say x for to-do and to-dos. Now we do need to give it a key. So yeah, we could do an index. Sometimes that can cause issues. Why don't we just apply an ID each time we create a new task or to-do. Okay. So for to-do and to-dos, render a list item. And that way I can bind the text of this label to the body of the to-do.
Okay. So once again, we give this a run. We add the to-do, but we haven't yet cleared the newToDo property. So that's our next step. This newToDo will return to an empty string. All right. Go to the store. And it works. Pick up lunch. And we're rolling.
Delete Todos from Array12:24
Let's come on back here. So what you might find as we're working on this, we're going to get to a point where it just feels kind of gross to do all of this in line. So at that point, as we learned in one of the earliest episodes, we will extract a dedicated function that we will call. And that function will return our object here. Okay. Let's go a little bit further though. How would I delete a to-do from an array? The easiest option is to use splice.
How would I delete a to-do from an array? The easiest option is to use splice. Let's say this.todos.indexOf the to-do in question. And then we could say once we have that index, we could then say this.todos.splice position one. All right. Let's see if it works. One more time. One, two, three. Let's get rid of one.
Track Completion and Active14:02
I'm not sure what term to use. They are saying active here, so my instinct is to keep it at that. Either one would work. Okay. So now, this is almost like a computed property or a subset of that todos array. So if I wanted to say, give me the todos, but specifically the todos where the completed status is still set to false. Okay. Well, how would we do that? The first step is we're not tracking whether the todo has been completed or not.
Well, how would we do that? The first step is we're not tracking whether the todo has been completed or not. So that's one of our first steps. When I add a new todo, we immediately set the completed to false. It's a brand new todo. It hasn't been completed. Next, I can use a getter here, a JavaScript getter, to simulate the functionality you might get from a computed property. So for example, if I said getActive, this would be one option. So this would be all of the todos filtered down to those where the completed property
So for example, if I said getActive, this would be one option. So this would be all of the todos filtered down to those where the completed property is set to false. So we could say this.todos.filter, and I only want to return the todo where the completed is set to false. So let's negate that. Okay. So now we have a way to get a subset of the todos, the active ones. Okay. So if we scroll back down, where was it?
Toggle Completion UI15:39
This is just adding a nicer visual to checking a traditional checkbox input. But when we do this, we're not updating anything behind the scenes. So why don't we do that now? Let's come on back and let's see where we are. So here's our list. Yeah. So here's our list item. This represents each one of these, and we're going to take a look at the input. There it is. So let's say when you click on me, we want to mark it as completed.
There it is. So let's say when you click on me, we want to mark it as completed. So we might say something like completeTodo, and then again, pass through the todo in question. So let's bring this back, and I think we're going to do one more method here, and then this is getting a little gross. completeTodo, and just to show you it works, I will often do things like this. Just as a sanity check, am I calling this method? So I click on it, and it works. Okay, so how do we mark a todo as completed? Well think about it.
The DOM was always the single source of truth, at least traditionally. So that's why, maybe depending on your age, it might be a little jarring to remind yourself that the data itself is the source of truth. Okay, but anyways, if we want to mark it as completed, then update completed to true. todo.completed is true. Done. But now, yeah, if we run it again, we are updating it. So in fact, I'll show you a little tip. If we select our Alpine component, let's have a look. Don't forget, you can do $0 to access the element that is selected.
If we select our Alpine component, let's have a look. Don't forget, you can do $0 to access the element that is selected. So one thing you'll find is there is this __x property that Alpine will add. So this is your Alpine component, and you can do things like give me the data. And that will be a proxy, but you can dig down to the handler and target. There it is. So there's your element. There's all your methods. Here's your array. And notice that completed is set to true.
Here's your array. And notice that completed is set to true. So actually, on this note, you can do some funky stuff where you could say, give me the todos. Give me the first one. And again, you're always going to get a proxy here. That's okay. That's how Alpine intercepts. But that would give you the data. So you could grab an element using a selector, and if you need to, you could dig down to
So you could do some funky things if you wanted to there. Anyways, the primary point of that was just to show when we clicked on it, it did mark the task as completed, but we don't have a visual to go along with it. All right. Well, don't forget, we did have this section here. List items get the class editing when edited and completed when marked as completed. Okay. So here's our list item. Let's use Alpine to bind a class. And we're going to give it completed if the todo is completed.
Let's use Alpine to bind a class. And we're going to give it completed if the todo is completed. It's so easy. So one more time, I click on it. We update that completed property, and it looks like their CSS will add a line through. Let's do another one. Mark it as completed. However, what if I want to do the reverse? Well, I should be able to select it again, but that's not actually working because right here, where did we put it?
Well, I should be able to select it again, but that's not actually working because right here, where did we put it? Complete todo, maybe that's not quite what we want. We want to toggle the completion status. So let's see. If we scroll back up, at the moment, all we're doing is updating a property. So often, I'm inclined to keep that in line. So let's come back here, and we had todo.completed was true. But yeah, it sounds like it's really a toggle. So we're going to set it to true if it's false, and if it's false, we'll set it to true.
But yeah, it sounds like it's really a toggle. So we're going to set $toggle to true if it's false, and if it's false, we'll set it to true. Or in other words, we'll set it to the opposite of what it currently is. One more time. Complete it and uncomplete it. All right. And actually, one more thing. One, two, three. If we did everything correctly, this should be reflected. So there are four active tasks, but as soon as I complete one, that should re-render.
This is just a simple explanation with no code.
