Skip to content

How To Use V-Model With Vuex

In my previous blog post “Learn Vuex Through A Tutorial“, I demonstrated how to implement state management using Vuex. As you surely know, a central theme of Vuex is its unidirectional data flow. This means two-way binding is a no-go! Unfortunately, this means the Vue directive v-model will not work with it out of the box. ๐Ÿ˜ฃ “Oh no! What am I gonna do!?. Don’t worry, I’ve got you covered. In this blog post, I’ll show you how to use v-model with Vuex.

In case you were wondering, no matter if you’re using Vue 3 or Vue 2, you’ll encounter the same issue.

Make your choice

There are two ways of tackling this problem: creating a two-way computed property or “modifying”, for lack of a better term, the v-model logic to accommodate Vuex. Both of these solutions work in strict ๐Ÿ‘ฎโ€โ™‚๏ธ mode.

Let me first explain the latter in more detail.

Solution 1: Modifying v-model logic

As you may or may not know, the v-model directive is essentially syntactic sugar. ๐Ÿญ It can be rewritten in a more verbose manner using two other directives, as seen below. In this example, the two-way binding happens by calling updateMessage on any input change and updating message with the input value.

<template>
  <input v-model="message" />
  <!-- Is the equivalent of -->
  <input :value="message" @input="updateMessage">
</template>

<script>
export default {
  data() {
    return {
      message: 'Chunk Bytes is awesome'
    } 
  },
  methods: {
    /* Behind the scenes */
    updateMessage(event) {
      this.message = event.target.value
    }
  }
}
</script>

Now that we know how v-model actually works under the hood, ๐Ÿš— we can make some simple changes to the code in order to make it work with Vuex. Let me show you!

<template>
  <input :value="message" @input="updateMessage">
</template>

<script>
export default {
  computed: {
    /* Use a computed property instead */
    message() {
      return this.$store.state.message
    }
  },
  methods: {
    /* Call a mutation instead */
    updateMessage(event) {
      this.$store.commit('updateMessage', event.target.value)
    }
  }
}
</script>

The first change we need to make is using a computed property (or getter) to retrieve the state data. Alright, step one done. โœ… Next, we need to alter this state by calling a mutation through a method function. You may of course dispatch an action and then commit the mutation, if you prefer to do so.

In case you were wondering, here’s what the mutation handler would look like in our store.

const mutations = {
  updateMessage(state, payload) {
    state.message = payload
  }
}

And really, that’s all there is to it! ๐Ÿ˜Š

Solution 2: Two-way computed property

“What’s the other solution you talked about?”. Glad you didn’t forget dear reader! As I previously mentioned, we can tackle this issue by constructing a two-way computed property using a getter and a setter (โš ๏ธ getter in this context is not referring to the actual getter function in Vuex).

<template>
  <input v-model="message">
</template>

<script>
export default {
  computed: {
    message: {
      /* By default get() is used */
      get() {
        return this.$store.state.message
      },
      /* We add a setter */
      set(value) {
        this.$store.commit('updateMessage', value)
      }
    }
  }
}
</script>

In this solution, we donโ€™t actually mess around with the v-model directive, but with the property itโ€™s bound to. By default, a computed property is a getter only. ๐Ÿ˜ฒ However, we have the option to also add a setter.

Inside of the computed property, we have a custom getter function called get() directly retrieving the state from our store. On the other hand, in order to alter the state, we use a setter called set(). This calls a mutation handler and passes in the input value that we automatically receive.

Review

Pay attention to the structure of both solutions. You’ll see that they are both very similar. In the first one, you essentially have two functions, one as a computed property, the other as a method. In the second case, you have the same functions, just named differently and part of a computed property. Hmmm… ๐Ÿง

Using a library

It might not come as a surprise to you that many others have come across this problem and evidently tried to come up with a solution of their own. The most popular library at the moment is vuex-map-fields, with over a thousand stars at the time of this writing.

Its documentation is quite well written in my opinion, so I don’t think a tutorial will be necessary (unless you ask me of course… ๐Ÿ˜‹). Nonetheless, knowing someone’s already made an easy to use solution should not discourage you from knowing the other alternative solutions I presented to you above.

In fact, you’ll sometimes run into a problem with the library and might have to resort to the old-fashioned solution, as I had to do once.


Did you find what you came for? I hoped you learned from this tutorial how to use Vuex with v-model. Want me to cover something else? Let me know in the comments below! Share, if you liked this post!

Published inVue.jsVuex

One Comment

  1. Tyler Hall Tyler Hall

    For what its worth, I through that vuex-map-fields was only all that useful in a very simple use case. When you have a lot of store modules, the abstraction of the plugin become more complex than the getter/setter verbosity. I’d be curious if others who have used it feel the same.

Leave a Reply

Your email address will not be published. Required fields are marked *