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

Install and Set Up Vuex0:00

Our app is functionally complete for the most part, but it's kind of a mess. And we have everything shoved in one large component. So let's refactor this and split it up into a more organized layout. The first thing that I want to do is install Vuex so we have a centralized state or store that I can push data to and reference in our different components. Opening up a terminal, let's clear everything out that's in there now. And we can install Vuex by running npm install --save-dev vuex. And because we are running Vue 3, I need to install the latest version of Vuex right now. So I'm going to include this at next version here. All right, let's get that installation running.

So I'm going to include this at next version here. All right, let's get that installation running. It went through fine, so we can clear out of this terminal and open up our main JS file. So now we need to include Vuex in this file. So we've got to import createStore method from Vuex. And then create an actual store instance. Our store is going to consist of three parts. We need a state, which returns an object consisting of our state, some mutations, and some actions. I believe we're going to use both mutations and actions, but I'm not sure.

some mutations, and some actions. I believe we're going to use both mutations and actions, but I'm not sure. Either way, I'll just keep these in here in blank. And if we don't need one of them, I'll get rid of it. Okay, and then instead of initializing our app here at the bottom, we need to create our app by creating a constant app. And that equals createApp. And then we need to tell our app to use the store that we created. And then we can mount it at our app endpoint with the id of app. Okay, so what do we have to store in our Vuex state?

Move App State to Store1:45

And then we can mount it at our app endpoint with the id of app. Okay, so what do we have to store in our Vuex state? Let's go back into our main app Vue component, and let's see what data we're storing in here right now. The editor I don't believe we'll need to store in the main state, as that can be stored in an individual editor component that we can create. The database I believe will be accessed in more than one component, so we'll use that. Same with notes and activeNote. And the isOffline flag.

Same with notes and active note. And the isOffline flag. So let's copy these or cut these out and add this to our state. And back in our App.vue file, let's see what happens in this created method. So we get the database and then populate it with the notes. And this is a perfect candidate to move to an action. So let's go ahead and copy this up to our actions area. We'll call it initialize or init. We have to pass in commits.

We'll call it initialize or init. We have to pass in commits. And then for our mutations, we need one for updating the database. So state.database equals database. This should be state and database. So then I should be able to say let database is getDatabase. And then we can just commit to update database the database. And then we'll need the same thing for the notes. So updateNotes, state.notes, state.notes equals notes.

So update notes, state notes, state.notes equals notes. Commit, update notes, notes. Actually commit notes reverse. But actually for this initial action, I don't want to create a function outside of the scope of this store here and then call it as a constant or something like that. So instead what I'm going to do is create two separate actions for initializing the database and initializing the notes. And then we're going to call them both in this main init method here.

for initializing the database and initializing the notes. And then we're going to call them both in this main init method here. And so what that looks like is we have init and then we have initializeDatabase. And this will contain the commit method. So we'll initializeDatabase and then we will commit to update the database whenever we get that database value back. And then likewise with the notes. So initNotes, commit, initialize the notes array,

So init notes, commit, initialize the notes array, commit, update notes, notes. So then up here what we end up doing is instead of having commits, we have dispatch that we're pulling in. And then we use it just like we would any other section of the application. So now we just say dispatch, init database, and dispatch init notes. And so now let's first initialize that database here.

Refactor Database Initialization4:59

and dispatch init notes. And so now let's first initialize that database here. So back in our app view file we can get rid of this created method. We're not using that anymore. And let's go down to the getDatabase method here. So we have this returning a new Promise and if we go to the main.js store, we really don't need to do that. Because whenever we dispatch initDatabase or initNotes, these can be asynchronous methods as they are.

Because whenever we dispatch initDatabase or initNotes, these can be asynchronous methods as they are. So we should just be able to add the code that's between this promise and just update it for the actual action. So let's cut this out of here. And we'll paste that into our initializeDatabase method. We initialize the database with the open method. On error, instead of rejecting, we're just going to console.error that out. And likewise with resolving, instead of resolving, we just use this commit statement down here.

And likewise with resolving, instead of resolving, we just use this commit statement down here. So on success, we commit update the database and we use target result. And so once our database is initialized, that will store the result of this database connection into the state's database attribute up here. And so likewise with notes. So let's go back to our app.vue file and grab everything in the getNotes promise here.

So let's go back to our app.view file and grab everything in the getNotes promise here. So like before, we're using the commit updateNotes here instead of the resolve. commit updateNotes, target result. And then instead of this database to pull in the database, it's stored in the state, so we use state.database. In order to access that, we pull in the object up here in these brackets. So now we can access the full state of our Vuex store and pull in the database and continue on with the transaction.

So now we can access the full state of our Vuex store and pull in the database and continue on with the transaction. But we need to make sure that the database is ready before we start making this note transaction. So in the initialize method, we can actually call a await on this. Because this is an asynchronous method to begin with, we can just use await to wait for it to finish up before we fire off the initNotes action. All right, let's see what else we can pull from that main component file. So we have getNotes done.

Add Note Saving Action7:21

All right, let's see what else we can pull from that main component file. So we have getNotes done. All right, how about saving a note? That will be another action that we won't need right away, but we can dispatch it later on. So let's cut what we have here in this saveNote method and we'll get rid of that. And then in our actions for our Vuex store, we'll create a new method called saveNotes. And we commit because we're going to be updating our state.

we'll create a new method called saveNotes. And we commit because we're going to be updating our state. And we're also passing in a payload of note. All right, so just like before, we're accessing the database, so we should also use state here. And then we have state.database. We make a transaction for notes. We get the request. activeNote is also a state object, so we turn this to state. We'll replace the reject with a console.error.

so we turn this to state. We'll replace the reject with a console.error. On success, we get the note as the result. We have to get the editor content though, which means we will need to pull the editor into our state up here. So let's make that quick change now. editor is null. Update editor. Okay, so this.editor becomes state.editor. Another rejection becomes console.error.

Okay, so this.editor becomes state.editor. Another rejection becomes console.error. And then on the update request, remember that we have to swap out the updated note that we did for all of the notes array that was in the component. Now, we don't have this.notes, but we do have the state.notes. So let's call notes equals state.notes. And then we're going to use that in place of all of the old this.notes. And then instead of resolve here, we commit to update notes to the new notes array.

Split App Into Components9:20

And then instead of resolve here, we commit to update notes to the new notes array. Okay, now I think it's time that we can start splitting some of this out into different components. So we have a components directory already. I'm spotting probably two different components that we can split this out to, if that makes sense. One is our sidebar, and one is our editor. Actually, let's make this three components. We'll do the sidebar, the editor, and the display of notes.

Actually, let's make this three components. We'll do the sidebar, the editor, and the display of notes. I'll put my terminal, cd into source/components. And we'll touch sidebar.view, editor.view, and notes.view. And let's do the sidebar first. So our sidebar is everything that's underneath here. So for now, we can just call sidebar. And we'll open up that view component. Dump this into the template. And back in our app.view file, we'll need to import this.

Dump this into the template. And back in our app.view file, we'll need to import this as sidebar from components/sidebar. And include it in our components list. And going back to the sidebar, let's see what references we have to any kind of events. All right, so on click, we have a showAllNotes. We have an addNewNote here. And then we have our v-for notes in notes with an openNote here. All right, so let's add in our <script> tag at the bottom.

And then we have our v4 notes in notes with an open note here. All right, so let's add in our script tag at the bottom. So for the notes, we need a computed property that returns this store.state.notes. That's easy enough. And then the method openNote in the old App.vue file, what did that do? So it set the content of the editor to the note and then set the active note property as the note. So this is easy.

and then set the active note property as the note. So this is easy. Did we do active note yet in main.js in the Vuex store? We did not. So we need to create another mutation called updateActiveNote. And pass in a note so that way the state of active note is equal to the note that we pass in. So now back in sidebar, we can create an openNote method. And all we have to do is call this store.commit updateActiveNote as note.

And all we have to do is call this store commit update active note as note. And what else do we need to do? We need to run a command on the editor to set the content as the note of the content. So we can just call the command directly on the store state that holds the editor. So this store state editor commands set content and the content of the note that's passed in. So that should work fine.

and the content of the note that's passed in. So that should work fine. So now back in our app.vue file, we can get rid of this openNote method here. And now we have showAllNotes. So likewise, similarly, we go back to the sidebar.vue view, create a new method, showAllNotes. And then we just make some slight adjustments. So instead of this editor, it's this storeState.editor. And instead of calling the activeNote as an empty object.

So instead of this editor, it's this store state editor. And instead of calling the active note as an empty object in our local data attribute, we need to set that in the state. So we can just say this store commits updateActiveNote is an empty object. And the last method that we needed was addNewNote. In our App.vue, that consisted of another promise because it's a transaction ran on the database. So this is one that I'm actually not going to have included in the sidebar component.

So this is one that I'm actually not going to have included in the sidebar component. And I'm instead going to let the Vuex store handle this just like our other database transactions. So let's go ahead and copy all of this code out of here, everything inside the promise. And we're going to put that in our main.js into an action called addNewNotes. And just like the other database transactions, we're going to need both commit and state.

And just like the other database transactions, we're going to need both commit and state since we're accessing a database. So we say state database transaction notes read write on transaction complete. We don't really need to do anything. So we can get rid of this whole onComplete method here. We initialize the note. Instead of this notes, we do state notes. And we add that note to the top of it.

Instead of stateNotes, we do stateNotes. And we add that note to the top of it. But remember, it's not wise to directly modify the state itself. We need to commit to the state a whole new object or a whole new array. So we should do let notes = state.notes. And then we say notes.unshift(newNote) and commit, update notes, notes. And likewise with the activeNote.

and commit, update notes, notes. And likewise with the active note. Instead of giving it the value of the note, commit, update active note, note. And then the transaction just adds the note to the database and we're good to go. And to actually use this back in the sidebar in the addNewNote method, we can run this storeDispatchAddNewNote. Alright, so I believe our sidebar component is all set.

we can run this store dispatch addNewNote. Alright, so I believe our Sidebar component is all set. And in the app.view file we can get rid of addNewNote and get rid of the getDatabase. So we have already slimmed down a decent amount here. The next one that I want to focus on is the Editor component. And that consists of basically everything that is in between these lines here. Now I do want to keep this v-if statement here,

that is in between these lines here. Now I do want to keep this if statement here, so I'm going to call editor, if, object keys, activeNote, length. And then down below we're going to have the notes component. So it'll be notes vues. So now we can cut this out of here.

No worries. Underneath the methods, computed, activeNote returns this store state, activeNote. And let's go ahead and open up our editor component and get this template added in with our code from before. Okay, so we can get rid of this v-if here, but we need to add this editorContent, which we had in the main app.vue file.

but we need to add this editor content, which we had in the main app Vue file. So let's go back to that, and we'll get this all set up. So we needed the editor, editor content, and starter kits imported. And then for the components, we needed the editor content. Now let's get rid of everything that we just pulled in there. And instead, let's import the editor. And the notes while we're here.

And instead, let's import the editor. And the notes while we're here. Now we don't need the editor data attribute anymore, because that's on the component level for the editor. So we'll need that here instead. And then on the mounted method is where we'll initialize that editor. And remember before how I was talking about that I want to be able to store the editor inside of the main Vuex state.

that I want to be able to store the editor inside of the main Vuex state. So let's do that. That's what we're going to do here. Instead of calling this editor on the data attribute, we should be calling let editor equals new Editor. And then after that's initialized, commit on this store, updateEditor, and pass in that editor variable.

update editor, and pass in that editor variable. So we can actually get rid of this whole data attribute at the top, and we'll add a computed property that returns the editor. So this editor we're passing into the editor content is synced up with the Vuex store. Any change that we make to it should reflect in our editor component. The last thing that we need to do, though, is this saveNote method in this component.

The last thing that we need to do, though, is this saveNote method in this component. And in our app.vue file, I think we might have already gotten rid of that. What did we do in the main.js file? Do we have saveNote? We do, we have saveNote here. All we have to do is dispatch this event with a note, and we can just replace the click event handler with store.dispatch('saveNote'). And we don't need to pass in a note,

with store, dispatch, save note. And we don't need to pass in a note, because I'm pretty sure that's supposed to take the active note. So going back to the main.js, yeah, this note's not even used. I don't know why I pulled that in. Let's go back to our app.vue file. We're almost done here refactoring this. The last part that we need is this notes list section. Let's go ahead and cut this code out. And this should be pretty straightforward.

Let's go ahead and cut this code out. And this should be pretty straightforward going into our Notes component. Let's create a new template. Paste in this code. The only part of this script we should need is a computed property called notes that returns this store state notes.

that returns this store state notes. We're just looping over the notes and displaying them in the main page. I think I might have missed one thing. Did I keep that in the state? Or did I want to keep that in the state? Okay, I did. So that's something that I'm just going to keep in the main app.vue file. It'll be initialized

So that's something that I'm just going to keep in the main app.vue file. It'll be initialized in the store and it'll determine the origin of the state if it's offline right away. And then we can update that by a store mutation. Update is offline. State is offline. So now back in our app.vue file this is offline reference. Instead of it being a data attribute, which there is no more

this is offline reference. Instead of it being a data attribute, which there is no more of those, it is going to be a computed property. So return this. storeState is offline. And now whenever our method is referenced like this syncUserData, if this is offline it's referencing the vuex store's attribute, not our local data store.

it's referencing the vuex stores attribute, not our local data store. Before I mount to destroy the editor, we will call another store action. Instead of doing this locally, so this store state dispatch we'll call it destroyEditor. And then we'll add that in as an action as well. Oh, we need the state.

as well. Oh, we need the state. So state editor destroy and commit update editor is now null. Okay, and then for the event listeners instead of setting the isOffline property that will be local to true or false, we can call a commit on the store

will be local to true or false, we can call a commit on the store to update is-offline as true or false. And the reason for that is that while we are just using the is-offline component in our local app.vue file to display this banner or not, if we had different components that use it, like let's say in the editor's methods or in the notes component that we

different components that use it, like let's say in the editor's methods or in the Notes component that we gave it a method to save notes instead of just displaying them we can pull in that central state attribute of isOffline and determine if we need to change our app's behavior because the user is offline without having to copy the functionality of event listeners or the navigator property. Okay, so this should be our app entirely refactored. I'm going to refresh our dev server just to be sure.

Fix Async Dispatch Error23:57

Okay, so this should be our app entirely refactored. I'm going to refresh our dev server just to be sure and see how this looks in the browser. Uh oh, we're getting an error. Cannot use keyword await outside of an async function but actions should be asynchronous so let's see what's going on here. Okay, so it looks like the problem is that instead of awaiting the dispatch for a database I should call a then for the resolve of the

instead of awaiting the dispatch for a database I should call a then for the resolve of the promise that the dispatch returns. And then inside of that is the code that I want to call whenever the dispatch is completed, which is this dispatchInitNotes. And then lastly to get everything started we need to fire off this initial init action in order to start dispatching the initDatabase and then the initNotes methods. So in order to do that all we do is

in order to start dispatching the initDatabase and then the initNotes methods. So in order to do that all we do is go down to the very bottom here and after we mount the app we'll call storeDispatch init. Alright, now let's save this and take a look in our browser. Alright, our application is working as expected and so now I can click on a node to go through them or create a new one. Just as expected.

Installing and setting up VuexSplitting our main component apartUsing mutations and actions

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