Should we use v-model to modify Vuex store?
Asked Answered
V

7

50

Hello I am beginner in Vue and I do have a problem that's really bugging me. I am wondering should we use v-model directive to modify vuex store? Vuex says that we should modify vuex store only by mutations but v-model makes everything easier and shorter.(I am asking because i couldn't find clear answer)

Virtuoso answered 28/2, 2019 at 14:37 Comment(0)
B
94

https://vuex.vuejs.org/guide/forms.html

When using Vuex in strict mode, it could be a bit tricky to use v-model on a piece of state that belongs to Vuex.

The "Vuex way" to deal with it is binding the <input>'s value and call an action on the input or change event.

Be sure to check out the simple "Two-way Computed Property" example on that page:

<input v-model="message">

computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}
Barratry answered 28/2, 2019 at 14:43 Comment(2)
Would this work for more complex data in the message?Disputant
Thx for the solution. I Modified it to use dispatch("SomeAction") rather than commit. Since using an action is the recommended way to do it.Fricative
G
11

I think another good option which hasn't been mentioned in any answer here is to use vuex-map-fields. In fact, the library author has written a very nice explanation for the library's usefulness. As per its GitHub page, to use the library you can do something like this:

In your Vuex Store, you can have a snippet similar to this:

import Vue from 'vue';
import Vuex from 'vuex';

import { getField, updateField } from 'vuex-map-fields';

Vue.use(Vuex);

export default new Vuex.Store({
  // ...
  modules: {
    fooModule: {
      namespaced: true,
      state: {
        foo: '',
      },
      getters: {
        getField,
      },
      mutations: {
        updateField,
      },
    },
  },
});

And in your component code, you can have something along the lines of this:

<template>
  <div id="app">
    <input v-model="foo">
  </div>
</template>

<script>
import { mapFields } from 'vuex-map-fields';

export default {
  computed: {
    // `fooModule` is the name of the Vuex module.
    ...mapFields('fooModule', ['foo']),
  },
};
</script>

Additional examples for various use cases are shown in the library's GitHub repository that I linked to in the first sentence of this answer.

Gina answered 4/7, 2020 at 7:20 Comment(0)
J
8

Above solution can also implemented with mutations:

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

<script>
import { mapMutations, mapState } from 'vuex';

export default {
  computed: {
    ...mapState({messageFromStore: 'message'}),
    message: {
      get() {
        return this.messageFromStore;
      },
      set(value) {
        this.updateMessage(value);
      }
    }
  },
  methods: {
    ...mapMutations('updateMessage')
  }
};
</script>
Johm answered 28/8, 2019 at 13:4 Comment(1)
I guess you mean <input v-model="tab"> on line 2Sepalous
G
3

My Solution to this was to use a getter to set value and @input to call the mutation.

<input
  type="text"
  :value="$store.getters.apartmentStreet"
  @input="value => $store.commit('apartmentValue', { handle: 'street', value })"
>

getters.js:

export default {
  apartmentStreet: state => state.apartment.street,
};

mutations.js

export default {
  apartmentValue(state, payload) {
    let oldValue = state.apartment[payload.handle];
    let newValue = payload.value;
    if (newValue !== oldValue) state.apartment[payload.handle] = payload.value;
  }
};

If you use this method be sure to check which event you want.

Guardrail answered 23/6, 2020 at 6:23 Comment(0)
M
1

I use this solution.

data() {
  return {
    formData: {
      username: '',
      email: '',
      bio: {
        firstName: '',
        lastName: ''
      },
      games: ['civ4', 'caesar3', 'homeworld', 'cataclysm'],
         
    }
  }
},
computed: {
  ...mapGetters({   //or mapState
    user: 'users'
  })
},
watch: {
  user(newValue) {
    this.formData.username = newValue.name;
    this.formData.email = newValue.email;
    this.formData.bio.firstName = newValue.bio.firstName;
    this.formData.bio.lastName = newValue.bio.lastName;
    this.formData.games = newValue.games.map(x=> { return x });
  }
},
beforeCreate: fucntion() {
  this.$store.dispatch('getUser');
}

And then you just regularly use v-model. It is important to make deep copy of object from store, like using map for array, and how i did stuff with object inside.

And, also you need to have initiated this user object in store also, with empty fields.

Magnesite answered 16/3, 2021 at 22:44 Comment(0)
P
0

Using getter and setter in computed properties works perfect. However in my case I have 'Reset' button which should revert back all unsaved changes. I have used watcher in combination with mapState:

  <h5>Edit User</h5>
  ...
  <q-input v-model="user_data.full_name" label="Full Name" />
  <q-input v-model="user_data.email" type="email" label="Email" />
  <q-checkbox v-model="user_data.is_active" label="Is Active" />
  ...
  <q-btn label="Cancel" @click="cancel" />
  <q-btn label="Reset" @click="reset" />
  <q-btn label="Save" @click="save" />
</template>
<script>
import { mapActions, mapState } from "vuex";
export default {
  data() {
    return {
      id: "",
      user_data: {},
    };
  },
  computed: {
    ...mapState(["user"]),
  },
  watch: {
    user(value) {
      this.user_data = { ...value }; // very important to copy object
    },
  },
  mounted() {
    this.id = parseInt(this.$route.params.id);
    this.getUserById({ id: this.id });
  },
  methods: {
    ...mapActions(["saveUser", "getUserById"]),
    save() {
      this.saveUser(this.user_data);
      this.$router.push({ name: "userList" });
    },
    reset() {
      this.user_data = this.user;
    },
    cancel() {
      this.reset();
      this.$router.push({ name: "userList" });
    },
  },
};
</script>

Printer answered 1/4, 2023 at 17:56 Comment(0)
R
-2

Yes you can but is not the best practice.

As the documentation say the state should be updated only inside mutation to keep the control over the state.

But if you really want to do it you can with:

v-model="$store.state.yourProperty"
Ralphralston answered 28/2, 2019 at 14:42 Comment(5)
yes, but why we shouldn't use v-model (because we should modify vuex store only by mutations) but what v-model is doing that it is not recommended to modify vuex store by v-model directive?Maryrose
<< The benefit of this convention is we can record all state mutations happening to the store and implement advanced debugging helpers such as mutation logs, snapshots, and history re-rolls / time travel. >> : vuejs.org/v2/guide/state-management.htmlRalphralston
@TristanDeoliveira What if someone doesn't care about the "advanced debugging helpers", because he's building a simple app? Will v-modelling store directly cause any other problems? When I started learning Vue I did use v-models to link store directly (I used getters though not state object) and I don't remember having any problems. Isn't the best practice an overkill for small apps?Mice
@Mice agreed, vuex should handle this behind the scenes...Nicolanicolai
@Mice I would argue that if it's only about a simple app it's not even worth discussing. In the other hand large projects have loads of asynchronous calls, events and watches. In such case, it's hard to track abnormal behaviours so keeping a strict flow will pay off.Uranie

© 2022 - 2024 — McMap. All rights reserved.