Recreate TodoMVC Example0:00
Now that I feel relatively comfortable with the core concepts of Vuex, I want to work through one of the examples here. This is a good way to learn. Again, it's not anything you're ever going to keep. You're going to work on it, and then you're going to immediately throw it in the trash when you're done. It's just to get your blood flowing, so to speak, to get yourself in the process and in the workflow of using a structure like this. So let's take a look at TodoMVC. Now, a lot of people will clone this and then kind of tinker around with it. Personally, I don't ever do that. I would always recreate it from scratch, because I don't know what it is, but there's something about having to physically type out each keystroke yourself allows you to commit it to memory much better than just cloning this and then tinkering around. Okay, so we have this as a reference. We can see there's a store directory.
Initialize Vuex Store0:38
each keystroke yourself allows you to commit it to memory much better than just cloning this and then tinkering around. Okay, so we have this as a reference. We can see there's a store directory. Here's where he news up the Vuex store. Okay, so let's start building this up from scratch. Once again, I'm using Laravel, not for the PHP side, but just for the compiling, for the structure, so that it's more familiar to most of the people on this site. So let's cd in there, and we'll use code here. Now, if we go to package.json, yes, it'll pull in Vue by default. So let's go ahead and do an npm install or yarn to pull that in. All right, and then of course, we also need Vuex. Great. So let's see, let's go into resources, assets, JavaScript. So we have our main boilerplate here. Let's now import our store. So we'll go ahead and create that. So we want store/index.js. And just in case you're not
a mutations file, and then he imports that here. So let's see. Yeah, so he has a dedicated file where he has his state, mutations, and then he exports each of those. I'm not going to worry about that in this case, but it's an option for you. So now we can see he has various mutations for changing a to-do, toggling it. Okay, okay. I feel ready for this. So let's come back and say export default a new Vuex store. So that looks good to start. Now within app.js, we import our store, and then we will make it available to all components. So the parent component, as well as all child components. Next, let's switch over to our welcome page, and we'll just put everything in here. So let me get rid of the boilerplate. And then all of this, and we'll have a div with an id of app. Finally, I will import the compiled file, like so. And then let's do npm run watch to keep an eye on changes and compile them down to this file. So we'll have our to-dos here, and
Add Todos to State3:07
of app. Finally, I will import the compiled file, like so. And then let's do npm run watch to keep an eye on changes and compile them down to this file. So we'll have our to-dos here, and I'm not going to match up with the example project at all. We have that as a reference, but we don't care about being one-to-one here. So then we're going to have some kind of to-dos here, and I assume we need as many to-dos as have been created. So we could say v-for to-do in to-dos, and then presumably the to-do component needs to know what to do. It needs the data for the to-do. So we'll pass that through. Okay, but at this point, we don't even have to-dos. So why don't we do that now? We can put that within our state. So we have to-dos, and that will be an array of to-dos, or you fetch it from a database, or localStorage, anything you want. So we'll say the body is go to the store and done. It is not completed at the moment, and then we'll duplicate this a
to-dos, or you fetch it from a database, or local storage, anything you want. So we'll say the body is go to the store and done. It is not completed at the moment, and then we'll duplicate this a couple times. This one will be buy groceries and finish homework, and we'll say buy groceries is done. Okay, so now we do have our to-dos, but it's contained within the store. So that means if we wanted to echo it out here, well, we could set up a computed property for that. So we could do something like this, computed, and we could say to-dos. Well, that's just going to delegate to the state or the store. This store state to-dos. All right, so that should compile behind the scenes. It does. So let's go to vuextodo.dev, and let's see what the problem is. Console, unknown custom element to-do. Yeah, of course, we forgot to do that. So we've created this custom element, but we haven't registered it with Vue. All right, so that's our next step. Why don't we
Register Todo Component4:51
unknown custom element to-do. Yeah, of course, we forgot to do that. So we've created this custom element, but we haven't registered it with Vue. All right, so that's our next step. Why don't we use example here, and I'm going to rename this file to to-do.vue, and then we will import it here. So the component is to-do, and that will reference to-do.vue. Okay, so if we switch back to Chrome and give that a refresh, now we see the example component, and we have three specific instances of it. Okay, next we can see a Vue tip. When we have a list of components, we need to set the keys. So what we can do is switch back to welcome, and we could say to-do, and then also give me the index, and then that will be our key. All right, so back to Chrome, refresh. We can ignore the CSRF token, that's a Laravel thing. But yeah, okay, so we're ready to display these to-dos, and what we'll do is right here, we'll place this within a list item, and to start, of course, we could say v-text would
that's a Laravel thing. But yeah, okay, so we're ready to display these to-dos, and what we'll do is right here, we'll place this within a list item, and to start, of course, we could say vtext would be toDo.body. But if we want to work with toDo, we have to accept it as a prop, and I think that should get us started. So if we give that a refresh, we have our three to-dos, and we are pulling that from the Vuex store. Now don't forget though, in app.js, in this case, we're using a computed property that just delegates. This is fine, but don't forget, you can also say import mapState from Vuex, and then down here, you could just say, well, let's just do this, computed: mapState, and specifically toDos. So now we have a computed property, this.toDos, and that's going to map to our state called toDos. So we should still get the exact same thing. Next, let's add a little checkbox. So if we come back to toDo, what we'll do here is display that within a label,
to our state called to-dos. So we should still get the exact same thing. Next, let's add a little checkbox. So if we come back to to-do, what we'll do here is display that within a label, to-do.body, and then before it, we could have an input where its check status is bound to to-do.done, and close that out here. Anyways, if we switch back, there we go. Buy groceries is the only one that is completed, and we can prove that because in our store, we set it to true. So if we do more than one is true, well, now both of those should be selected, but let's bring that back. Now, what if we wanted a button that would complete all to-dos? Okay, let's go to app.js. I'm sorry, welcome, and then right here, we'll have some button that will say complete all, something like that. Okay, so if we refresh, let's do a quick bit of styling if you don't mind. So we'll say on the body, how can we do this quick? Let's do flexbox,
that will say complete all, something like that. Okay, so if we refresh, let's do a quick bit of styling if you don't mind. So we'll say on the body, how can we do this quick? Let's do flexbox, justifyContent to the center. That's good. So let's just store it right up here. Come back, give that a refresh. Okay, and then I want the complete all button to be right up here. So let's do this. Let's say div level. This is like a mini component I often create. So we give it a class of level, and then what we can do, say display flex, and then alignItems to the center. Finally, I'm going to have just a class to add some marginRight because you'll get a lot of use out of that. Okay, so come back, refresh. Yeah, now they're next to each other, and I could always say right here, class mr1. Okay, good enough for my needs. So now when we click on this button, we want to mark every single to-do as complete. Okay, so it sounds like we need to
Complete All Mutation8:16
and I could always say right here, class mr1. Okay, good enough for my needs. So now when we click on this button, we want to mark every single to-do as complete. Okay, so it sounds like we need to say something like click. So let's think about it. What would need to happen in order to mark every single to-do as complete? Let's go into the state. It sounds like we would need to filter through all of our to-dos and mark the done property to true, and when we do that, everything should reevaluate and render to reflect the new state. Makes sense? Okay, so let's add a mutation here. completeAll, and that will accept the state. And yeah, we could just say state.to-dos.forEach((to-do) => to-do.done = true). All right, but yeah, right now if we give this a refresh, yep, it can't find completeAll. It's not defined on the instance. So we need to do that exact same thing again where we map the mutation. So let's do this. mapState and also mapMutations,
a refresh, yep, it can't find complete all. It's not defined on the instance. So we need to do that exact same thing again where we map the mutation. So let's do this. Map state and also map mutations, and then we'll delegate here. All right, come back, give that a refresh, and now if we click on this, ta-da, every single to-do is marked as complete. And we can even take a look at that. done is true, done true. Now we're not persisting it, so if I give it a refresh, it's not being persisted, but you can do an AJAX request, you can do local storage. We don't care about that at all for this video. Now next, what about the option to delete a to-do? Okay, so let's go back to to-do, and then once again, we're being a little clumsy here with our layout, but that's okay. So we're going to have a little button here. If I click on it, it should remove or delete the to-do. So we'll say when you click on it, delete to-do. But now if you think
Delete Todo Mutation9:57
layout, but that's okay. So we're going to have a little button here. If I click on it, it should remove or delete the toDo. So we'll say when you click on it, delete toDo. But now if you think about it, if you just had a method defined on your store like delete toDo, well, at the moment, we don't exactly know which toDo should be deleted, right? So why don't we pass that through like so. Now any argument will be accepted here. And for things like this, if you want to remove an item from this array, splice is a good option for that. So you could say state.toDos.splice, and then just look for the index of the current toDo. So start there, and we're going to splice or remove exactly one item from the array. That's how that works. So if we come back to Chrome, go to the console, we try to delete one. Yeah, we have the exact same thing again, where we have to map delete toDo. Okay, so on toDo, we have to once again import mapMutations.
go to the console, we try to delete one. Yeah, we have the exact same thing again, where we have to map deleteToDo. Okay, so toDo, we have to once again import mapMutations from vuex, and then methods, mapMutations, deleteToDo. Okay, refresh. If I remove it, now we've stripped that or spliced it out of the underlying array. So if we take a look, now we have exactly one item, when before we had three. Now the next thing, if we click on one of these, yeah, you'll see right now it doesn't do anything behind the scenes. You're just checking a box. So ideally we want to, for example, when I click on this, done needs to turn to true, and maybe we even have some styling that represents that. So let's see what that might look like. Let's add a class up here, another one, and we'll say I want to add a class of isComplete if toDo.done. And then down here we could add the styling directly.
Toggle Todo Completion11:42
might look like. Let's add a class up here, another one, and we'll say I want to add a class of isComplete if todo.done. And then down here we could add the styling directly. So todo.isComplete will have a color of gray. Okay, so if we give that a refresh, yeah, you just have a little more indication that this is completed. So now when I click on this one, we need to update the todo to be complete. Okay, so it sounds like right here we need to say, well, when it changes, so when you click on the checkbox, we should toggle the todo and then map that, and then finally declare it on the store. So right here we'll say toggleTodo. Now in this case you can see every single time we do state.toDos. We could also use destructuring if you want. So you could do things like this, toDos, and that's just your way of saying I want to destructure state and all I really want out of it is the toDos property.
Okay, so let's give that one a shot now. Refresh. If we click on it, there we go. So we mark it, we then update the to-do to mark that it has been completed, and then once again within to-do we are mapping this class to that particular state. So everything re-renders on the fly. And we can delete it. Oh, whoops, state is not defined. We must have missed a reference state. One more time. Complete. Complete. Yeah. Okay, all of this is working pretty well. Now another thing we might want to add is, well, if every task or to-do has been completed, it's kind of weird to display this button because it won't do anything. There's nothing for it to do. So why don't we not show that if everything is complete here. So let's see, back to the welcome page. So maybe we could say v-show if not all complete, or all to-dos complete. So if all the to-dos are not complete, then we can display this button. But if they are, we don't need to display.
Hide Button When Done14:06
page. So maybe we could say v-show if not all complete, or all to-dos complete. So if all the to-dos are not complete, then we can display this button. But if they are, we don't need to display this at all. Okay, so let's try this out. So it sounds like we now need a computed property here. So right off the bat, this is no longer going to compile, right? Because we have that thing that we had earlier where our Babel compilation doesn't support this spread operator. So let's do spread. What is it? I forget. I've forgotten it already. Object.rest.spread.transform is what we want. So let's go ahead and pull that in. And then you can see we can update our .babelrc file, which will be merged with Laravel Mix, and it'll give us support. So let's create within the project root .babelrc, paste that in, and then once again boot up npm run watch. And that should take care of it. Okay, great.
and it'll give us support. So let's create within the project root .babelrc, paste that in, and then once again boot up npm run watch. And that should take care of it. Okay, great. So anyways, we have a new computed, what was it called? All to-dos complete. Or, you know what, all completed. Let's just bring it back to that. Okay, so how can we determine if all to-dos have been completed? We could fetch the to-dos from our state, and then let me know if every single to-do is done. Now if you're not familiar with every, this is going to return a boolean. So it will return true if every item within to-dos has a done property that returns true. So if to-do.done is true for every single to-do, then that is what we will return. Okay, so let's go ahead and run that. But if one of them is not completed, then it will return false. Does that make sense?
then that is what we will return. Okay, so let's go ahead and run that. But if one of them is not completed, then it will return false. Does that make sense? So let's come back to Chrome, complete, three, and now you'll notice we no longer see it. And if we take a look at this, all completed is set to true, but if we unmark it, now it becomes false. Okay, so that's pretty easy. But now if we come to app.js, we can either leave this here, which is fine, or once again we learned about getters. So if this is something that you might reach for in multiple places, you could extract this into your store into a getters section. It's just a matter of whether it needs to be reused, as I understand it. Okay, but anyways, the final step, let's just add a section to add a new to-do, and then we'll call it a day. Okay, so in welcome, right here, we'll have a new section and input with a placeholder of do this.
Add New Todo Input16:28
the final step, let's just add a section to add a new to-do, and then we'll call it a day. Okay, so in welcome, right here, we'll have a new section and input with a placeholder of do this. All right, let's see. And it looks horrible, but it's fine. So now we want to say when you type into it and you hit return, we need to push to that toDos array within our store. So we could say listen for when you hit the enter key, and we will add a new to-do. Okay, so let's see on app.js, same thing. We could map addToDo, but you know what, in this case, let me show you. In this particular case, we could say addToDo, but we need access to the value that the user typed in, and then we would also need to do things like clear out the input once you hit return, right? So why don't we go back and we'll say right here, we'll put this, we'll use the spread operator to merge that, and then we'll say
things like clear out the input once you hit return, right? So why don't we go back and we'll say right here, we'll put this, we'll use the spread operator to merge that, and then we'll say addToDo. This will accept the event, and yeah, then here we could delegate. And you'll remember if we need to commit a mutation, so we still want to call this mutation here, well we do that by saying this.store.commit, and then we'll give it the body of the new to-do. So we'll make sure that we accept that. Okay, so now at this point, we could push to the toDos. So I could say once again, toDos.push a new one where the body is equal to what was passed in, and then done will be false. Okay, so now back in app.js, if you add a to-do, we will commit that mutation, we will get the body, and that would be, well, let's see, index.js. I'm sorry, unwelcome. So our input event here, we just need to get the target.value. So just basic JavaScript here. That would be what
