Confused about Vuex modules - vue.js

I'll try and lay this out as cleanly as possible:
I have a Vue component that I'm rendering called World.vue:
<template>
<v-container>
This is the whole world
<v-btn #click="test()" />
</v-container>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
computed: {
...mapState('drawers', ['isEquipmentOpen']),
},
methods: {
...mapActions('drawers', ['test']),
},
};
</script>
And I have a module in for the Vuex store called drawers (drawers.js):
export default {
namespaced: true,
state: {
isEquipmentOpen: false,
},
actions: {
test(state) {
console.log(state.isEquipmentOpen);
state.isEquipmentOpen = !state.isEquipmentOpen;
},
},
};
What I don't understand is why when test() is called, it can't access isEquipmentOpen (logs a value of undefined). How can I manipulate isEquipmentOpen?

You cannot mutate state values from actions.
You'll need to create a mutations property, create a setter method in it
and call it in order to change the value.
Pass a { commit } parameter to method in actions property.
You don't need to pass the state parameter in actions methods.
Make sure that the string value in the commit() is the same as the setter name.
export default {
namespaced: true,
state: {
isEquipmentOpen: false,
},
actions: {
test({ commit }) {
console.log(state.isEquipmentOpen);
commit("setIsEquipmentOpen");
},
mutations: {
setIsEquipmentOpen: state => state.isEquipmentOpen != state.isEquipmentOpen
},
};
Hope you understood the answer!

Related

how to use v model if using vuex

how to use v-model on vuex,
in this case only to show modal box when button is clicked
in this vuex also have separate to module
this is index module
import createPersistedState from 'vuex-persistedstate'
import Vue from 'vue'
import Vuex from 'vuex'
import authentication from './authentication'
import products from './products'
import projects from './projects'
Vue.use(Vuex)
export default new Vuex.Store({
strict: true,
state: {
baseUrl: '/api',
// baseUrl: 'http://localhost:3333/api',
},
mutations: {
},
actions: {
},
modules: {
authentication,
products,
projects,
},
plugins: [
createPersistedState(),
],
})
and this is my module
import HTTP from '../http';
export default {
namespaced: true,
state:{
dialog: false,
},
getters: {
dialog(state){
console.log(state.dialog)
return state.dialog
}
},
mutations: {
closeCard(state){
state.dialog=false;
console.log(state.dialog);
}
}
}
i have try map state and map getter both not function
and this is my vue code
v-model="dialog"
width="500"
i have try use map state or map getter but both not working
import Test from '/components/Test'
export default {
components:{
Test
},
computed: {
dialog:{
get() {
return this.$store.state.dialog.products
},
},
map State('products',[
'dialog'
]),
},
methods:{
map Actions('products',[
'close Card',
])
}
}
and this is my error
Computed property "dialog" was assigned to but it has no setter.
found in
Directly binding a computed property to a v-model is a bad practice as a computed value doesn't have a default setter.
you will need to implement a setter for your computed value. you can read about it here.
you should implement a state action that changes the value of the dialog property and then call it in your setter (and set it to true or false, depending on the case.
Actually there are typo's in your code while called vuex handlers
import {mapState, mapActions } from 'vuex'; // check this line
import Test from '/components/Test'
export default {
components:{
Test
},
computed: {
dialog:{
return this.dialog;
},
...mapState('products',[ // check this line
'dialog'
]),
},
methods:{
...mapActions('products',[ // check this line
'closeCard', // check this line
])
}
}
Also note that you shouldn't directly bind a computed property to a v-model

Why action of Vuex returns a promise<pending>?

I have an action in Vuex actions which commit a mutation that it take a payload from the component, that is a number of the index for returning an object, it works fine on Vuex js file meaning that shows the selected item on the console, as I said it gets index from the payload,
but on the component, it gives me Promise <Pending>, why that's happening? for now, I do not use any API for my Nuxt/Vue app, but I will, and for now, I just want to know why this is happening and what is the best solution for solving this
Here my Vuex codes:
export const state = () => ({
articles: [
{
uid: 0,
img: 'https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/1.jpg',
link: '/articles/1',
},
{
uid: 1,
img: 'https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/2.jpg',
link: '/articles/2',
},
],
})
export const getters = {
getArticles(state) {
return state.articles
},
}
export const mutations = {
getSpeceficArticle(state, payload) {
return state.articles[payload]
},
}
export const actions = {
getSpeceficArticle({ commit }, payload) {
commit('getSpeceficArticle', payload)
},
}
and here my component codes:
<template>
<div class="article">
{{ getSpeceficArticle() }}
<div class="article__banner">
<img src="" alt="" />
</div>
<div class="article__text">
<p></p>
</div>
</div>
</template>
<script>
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
// computed: {},
methods: {
async getSpeceficArticle() {
return await this.$store.dispatch('articles/getSpeceficArticle', 0)
},
},
}
</script>
actions are used to update the state they are like mutations but the main difference between them is that actions can include some asynchronous tasks, if you want to get a specific article at given index you should use a getter named getArticleByIndex :
export const getters = {
getArticles(state) {
return state.articles
},
getArticleByIndex:: (state) => (index) => {
return state.articles[index]
}
}
then define a computed property called articleByIndex :
<script>
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
computed: {
articleByIndex(){
return this.$store.getters.articles.getArticleByIndex(0)
}
},
methods: {
},
}
</script>
#Mohammad if you find yourself using a lot of getters/actions etc from Vuex and they're starting to get a little wordy, you can bring in mapGetters from Vuex and rename your calls to something a little more convenient. So your script would become,
<script>
import { mapGetters } from 'vuex'
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
computed: {
articleByIndex(){
return this.getArticleByIndex(0)
}
},
methods: {
...mapGetters({
getArticleByIndex: 'articles/getArticleByIndex',
})
},
}
</script>
You can add ...mapGetters, ...mapActions to your computed section also.
since there is no web service call in vuex action, try to remove async and await keywords from the component.
Later when you add a webservice call than you can wrap action body in new Promise with resolve and reject and then you can use async and await in component. let me know if this works for you.

Two way data flow on Vue component

I want a selector in a Vue component to update when the stored value in Vuex is updated. Here is a simplified version of the vue component:
<template>
<v-autocomplete
outlined
dense
v-model="team"
label="Select Team"
:items="teams"
item-text="name"
item-value="_id"
return-object
class="mx-3 mt-3"
#change="selectTeam"
></v-autocomplete>
</template>
The JS:
<script>
export default {
name: 'NavDrawer',
data() {
return {
team: null,
teams: [],
};
},
async created() {
this.team = this.$store.getters['teams/allTeams'].find(
(t) => t.name === this.$route.params.team,
);
this.teams = this.$store.getters['teams/allTeams'];
},
methods: {
async selectTeam() {
if (this.team) {
await this.$store.dispatch('editTeam/selectTeam', this.team);
this.$router.push(`/team/${this.team.name}`);
} else {
this.$router.push('/');
}
},
},
};
</script>
And the Vuex store:
export default {
namespaced: true,
state: () => ({
editedTeam: {},
}),
mutations: {
selectTeam(state, team) {
state.editedTeam = team;
},
resetTeam(state) {
state.editedTeam = {};
},
},
actions: {
selectTeam({ commit }, team) {
commit('selectTeam', team);
},
resetTeam({ commit }) {
commit('resetTeam');
},
},
getters: {
getSelectedTeam: (state) => state.editedTeam,
},
};
I'm not sure if it matters, but this vuex store is passed into an index file to create the Vuex.Store -- this is working correctly and is not the issue.
I want the team stored in editedTeam to reactively update the team selected in the v-autocomplete component, when a new editedTeam is selected elsewhere in the application. I want the v-autocomplete selector to send a new team to editedTeam when it is selected. I think I should be using mapState for this and storing them as a computed value -- but nothing I've tried has worked. I find the vuex documentation to be lacking in good examples.
Thanks!

Vuex mapped state always undefined in component

I have a namespaced Vuex store module, and I am trying to access that module's state in a component (via mapState) to set a default value in my data. However, the "mapped state" is always undefined in my component.
My store is:
cartDetail.js
export const defaults = {
id: null,
items: [],
},
const state = { ...defaults, };
export default {
namespaced: true,
state,
}
and in my component, I have:
<script>
import { mapState, mapActions, } from 'vuex';
data() {
defaultSelected: this.cartDetail.items[0],
},
computed () {
...mapState('cartDetail', ['cartDetail,'],),
setDefaultSelected () {
return this.cartDetail.items[0];
},
},
created () {
this.cartFetch(userId);
}
</script>
Even when I console.log(this.cartDetail); in either my setDefaultSelected computed, or in created hook, it is undefined! Any help would be much appreciated!!
your data section looks incorrect. It should look like this:
data() {
return {
defaultSelected: this.items[0]
}
},
In mapState you should indicate state props (and their module name if they are not from the root state). I assume that carDetail.js is a store module.
computed () {
...mapState({
id: state => state.cartDetail.id,
items: state => state.cartDetail.items
}),
setDefaultSelected () {
return this.items[0];
},
},

Changing a vuex state from a different component?

I have a component (modal) which relies on a store. The store has the state of the modal component - whether it is active or not.
I need to be able to call this modal to open from other components or even just on a standard link. It opens by adding an .active class.
How can I change the state of the store - either by calling the stores action or calling the modal components method (which is mapped to the store).
Modal Store:
class ModalModule {
constructor() {
return {
namespaced: true,
state: {
active: false,
},
mutations: {
toggleActive: (state) => {
return state.active = ! state.active;
},
},
actions: {
toggleActive({ commit }) {
commit('toggleActive');
},
},
getters: {
active: state => {
return state.active;
}
}
};
}
}
export default ModalModule;
Vue Component:
<template>
<div class="modal" v-bind:class="{ active: active }">
<div class="modal-inner">
<h1>modal content here</h1>
</div>
<div class="modal-close" v-on:click="this.toggleActive">
X
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
computed: {
...mapGetters('Modal', [
'active',
])
},
methods: {
...mapActions('Modal', [
'toggleActive',
]),
}
}
</script>
And somewhere else I want to be able to have something like:
<button v-on:click="how to change the state??">OPEN MODAL</button>
Edit:
Here's the store:
import Vuex from 'vuex';
import ModalModule from './ModalModule';
class Store extends Vuex.Store {
constructor() {
Vue.use(Vuex);
super({
modules: {
Modal: new ModalModule(),
}
});
};
}
You do not need an action for your particular usecase . You just just define a mutation as you are just changing the boolean value of a property in a state. Actions are for async functionality. You usecase is just synchronous change of Boolean value
So you can do
<button v-on:click="$store.commit('toggleActive')">OPEN MODAL</button>
EDIT:
Just export a plain object
const ModalModule = {
namespaced: true,
state: {
active: false,
},
mutations: {
toggleActive: (state) => {
return state.active = ! state.active;
},
},
actions: {
toggleActive({ commit }) {
commit('toggleActive');
},
},
getters: {
active: state => {
return state.active;
}
}
}
export default ModalModule;// export the module
Even remove the class based definition of the store
import Vue from 'vue'
import Vuex from 'vuex';
import ModalModule from './ModalModule';
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: {
ModalModule
}
});
And change it like this in you component for mapping of the mutation (<MODULE_NAME>/<MUTATION_NAME>)
...mapMutations([
'ModalModule/toggleActive'
])
You can access the store from your components via this.$store. There you can call your actions and mutations. So
<button v-on:click="$store.commit('your mutation name', true)">OPEN MODAL</button>