DEV Community

Cover image for Using v-model to bind user input to state in the Vuex store
Woet
Woet

Posted on • Originally published at woetflow.com

Using v-model to bind user input to state in the Vuex store

📋 Table of contents

  1. Creating the store
  2. Using value and input bindings to manage state
  3. Using v-model with computed setters to manage state

⚙️ Code

The example project used for this blog post can be found in this repository on Github: woetflow-demo-user-input-vuex-state

All this app contains is a component with the input and a component that prints the value from the state. It uses vue-tailwind for some styles and basic components like the text input.


1. Creating the store

We'll start off by creating the store, since this will look the same for both methods.

src/store/index.js:

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    values: {
      example: "Change my value to see the state update!"
    }
  },
  mutations: {
    setValues(state, values) {
      state.values = Object.assign({}, state.values, values);
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

So the store is quite straightforward: we create a values object with the example property set to a default value.

We're then creating a mutation that when commited, will set the state's values object to a newly created object. This new object uses the state's values object and the values object from the parameters as sources to set its properties.

2. Using input event and value bindings to manage state

One solution to bind user input to a piece of state in the Vuex store is using the input event and a value binding. This works, but it's not as intuitive as the usual way of keeping track of user input with v-model.

As you can see from the practical example here below, if you have lots of inputs it could start looking quite messy.

src/components/events/EventsExample.vue:

<template>
  <div>
    <t-card
      header="This input uses the input event and a value binding to manage its state in the Vuex store"
    >
      <t-input-group>
        <t-input
          :value="values.example"
          @input="updateExample"
          id="example"
          name="example"
        />
      </t-input-group>
      <PrintStateEvents />
    </t-card>
  </div>
</template>

<script>
import { mapState } from "vuex";
import PrintStateEvents from "@/components/events/PrintStateEvents";

export default {
  name: "EventsExample",
  components: {
    PrintStateEvents
  },
  computed: mapState(["values"]),
  methods: {
    updateExample(example) {
      this.$store.commit("setValues", { example: example });
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

So this would work, but there's a better way.

3. Using v-model with computed setters to manage state

In the previous example, we couldn't simply use v-model on the input because the store state must be immutable and v-model is two-way data binding. But luckily, computed setters are here to help!

So as you'll see from the code example below, when using v-model on the input, we don't need to make separate methods anymore and can remove the corresponding input event and value binding. All we to need is make a computer setter that will commit a mutation to the store and a computed getter to read it.

src/components/setters/SettersExample.vue:

<template>
  <div>
    <t-card
      header="This input uses v-model with computed setters to manage its state in the Vuex store"
    >
      <t-input-group>
        <t-input
          name="example"
          v-model="example"
        />
      </t-input-group>
      <PrintStateSetters />
    </t-card>
  </div>
</template>

<script>
import { mapState } from "vuex";
import PrintStateSetters from "@/components/setters/PrintStateSetters";

export default {
  name: "SettersExample",
  components: {
    PrintStateSetters
  },
  computed: {
    ...mapState(["values"]),
    example: {
      set(example) {
        this.$store.commit("setValues", { example });
      },
      get() {
        // Or remove mapState and use this.$store.state.values.example
        return this.values.example;
      }
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

And that's all there is to it! You can now happily apply v-model again combined with your Vuex state.

Top comments (0)