Vuelidate and Vuex

For our form to continue to work with Vuelidate alongside Vuex, we are going to have to make some adjustments to how we have our data set up for two-way binding on our inputs. Don't worry, we'll take it step by step. Now that we have Vuex incorporated into our app, we want our form to use our global state instead of the local state we had in our data() { form: {...} } inside App.vue. So, we need to make some changes in our template to tell the two-way binding to use Vuex instead.

We are going to remove all of the v-model statements from the inputs in our form. Instead, we are going to manually create our two-way bindings by setting up the :value bind and the @input listener.

First, we will create a new method called updateUser, which will receive two parameters, as follows:

  • The first one will be property in our form that is getting updated, for example, firstName or lastName.
  • The second parameter will be the value that this new property will receive.

So, let's start by adding this new method to our App.vue component:

updateUser(property, value) {
this.$store.dispatch('updateUserData', {
property,
value
});


this.$v.form[property].$touch();
}

This method will dispatch a new action that we will create in a moment called updateUserData; it will send a payload with the property and the value that the method got.

Let's stop for a minute and look at the second statement. Since Vuelidate will no longer be hooked into our local state, it is going to need us to tell it when to recalculate the dirty state of the input and to check for errors in validation. 

Since the updateUser method will be in charge of making the changes to our global state, we will access Vuelidate's object for this input through $v.form[property] and force $touch() on it.

Now that our state will be global, we don't need our form: {...} local state anymore, so you can go ahead and delete it. Your data() prop should now look like the following:

data() {
return {
loveOptions: [
{ label: 'Fun to use', value: 'fun' },
{ label: 'Friendly learning curve', value: 'curve' },
{ label: 'Amazing documentation', value: 'docs' },
{ label: 'Fantastic community', value: 'community' }
]
}
},

However, for Vuelidate to be able to access our global state, we are going to need to use a Vuex helper function to map it to computed properties. In this case, we want to use mapState. Imagine if you had to create a computed property for each one of our user properties, you would have to go down the list and create a lot of duplicated code that looked like the following example:

firstName() {
return this.$store.state.user.firstName;
}

Imagine having to do this for all the properties of your form, could get tedious fast, right?

In these cases, Vuex has some handy map functions that we can leverage, so let's go ahead and import mapState to the top of our App.vue file:

import { mapState } from 'vuex';

Next, we will add a computed prop to our App.vue component and use the mapState function to map our whole state to the computed properties:

computed: {
...mapState({form: 'user'}),
},

We are going to pass an object to mapState to tell the function exactly what part of our whole global state we want to map to our computed properties. In this case, we are telling it to map everything inside the user global state to a local form. Since the user is an object with several child properties, it will create a binding for each one of them, so that when App.vue calls, for example, this.form.firstName, it will be found in the global state in this.$store.state.user.firstName. Awesome, right?!

Keep in mind that mapState returns an object, so we can use the JavaScript ES6 spread operator here to merge the newly created into our computed: {} prop. This is incredibly handy if you want to add some more computed properties later, which are not mapped from Vuex.

If you want to learn more about the spread operator, please refer to the following article: https://dev.to/marinamosti/understanding-the-spread-operator-in-javascript-485j.

Before we go and work on the updateUserData action, let's make the v-model change that we discussed to our inputs. Remove all of the v-model statements, and replace them on each one as follows:

<BaseInput 
label="First Name:"
:value="$store.state.user.firstName"
@input="updateUser('firstName', $event)"
:validator="$v.form.firstName"
/>
<BaseInput
label="Last Name:"
:value="$store.state.user.lastName"
@input="updateUser('lastName', $event)"
:validator="$v.form.lastName"
/>
<BaseInput
label="Email:"
:value="$store.state.user.email"
@input="updateUser('email', $event)"
:validator="$v.form.email"
type="email"
/>
<BaseInput
label="The URL of your favorite Vue-made website"
:value="$store.state.user.website"
@input="updateUser('website', $event)"
:validator="$v.form.website"
/>
<BaseInput
label="Telephone"
type="text"
:value="$store.state.user.telephone"
@input="updateUser('telephone', $event)"
:validator="$v.form.telephone"
:mask="'(###)###-####'"
/>
<BaseSelect
label="What do you love most about Vue?"
:options="loveOptions"
:value="$store.state.user.love"
@input="updateUser('love', $event)"
:validator="$v.form.love"
/>

The :value property will bind to our global state, that is, the one we created at the beginning of this chapter. The $store property is accessible globally through our application thanks to Vuex, and we can use it to access the state directly, even in our templates. The @input listeners will point directly to updateUser, set the property as a string, and pass the payload of the $event as the value.

Head over to main.js; we have to create the new action that our updateUser method is calling. We are going to leverage our already existing mutation, updateUser, to update one of the properties of the user. You could also refactor this into a mutation that specifically just updates one of the properties instead of overwriting the whole object. In this case, the object is very small, and the performance is not a concern. Add the following action to your store:

updateUserData(context, payload) {
const userCopy = {...context.state.user};
userCopy[payload.property] = payload.value;
context.commit('updateUser', userCopy);
}

On the first line of the action, we are going to make a shallow copy of our user state by using ES6's spread operator. It's important to keep in mind that the state should never be changed outside of a mutation, so if we assign the new property value directly to the user in our state, we would be doing exactly that.

After making the copy, we set the property to the new value and call commit on our updateUser mutation. Go back to your browser and reload the page; make sure you have the dummy Mockoon API running so that our Axios calls work and check out the results:

That's it! With these few changes to our application, we have successfully made it so that not only do we have global state ruling over our form, but we also leverage the flexibility and power of Vuelidate to hook into the global state directly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.23.101.60