vuex mapped namespaced action "is not a function," error - javascript

I am importing a Vuex module. Everything works fine if I don't namespace it, but when I do namespace it is reports that the action is "not a function." when called.
index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import MainWindow from './modules/MainWindow';
import ArtEditor from './modules/ArtEditor';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
MainWindow,
ArtEditor
}
});
ArtEditor module:
const state = {
selectedColor : "#FFFFFF"
}
const getters = {
selectedColor: state => state.selectedColor
};
const actions = {
selectColor({commit}, newColor){
commit('selectColor', newColor);
console.log(newColor)
}
};
const mutations = {
selectColor: (state, newColor) => state.selectedColor = newColor
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
};
in Vue component:
import {mapActions, mapGetters} from 'vuex';
...
methods:{
...mapActions(['ArtEditor/selectColor']),
colorChanged(color){
this.selectColor(color);
}
},
...
when the color is changed, it throws the error this.selectColor is not a function. Most of the documentation on namespaced modules either doesn't import external Vuex modules, or uses dispatch() instead of mapActions(), I really don't know what I'm doing wrong.

Your syntax is off, you need to either do:
...mapActions(['ArtEditor/selectColor']),
colorChanged(color){
this['ArtEditor/selectColor'](color);
}
Or separate the action path while mapping:
...mapActions('ArtEditor', ['selectColor']),
colorChanged(color){
this.selectColor(color);
}
See binding helpers with namespace.

Related

Call Vue store action through browser console

I'm trying to create a webapp using VUE Vite with a router and store. The getter function in the vue file works fine. I have access to the chatMessages stored in the store.js file.
My problem is that I need to call the addMessage Action from the store.js file in the dev console using the browser.
Question: How could I archive this?
On older vue versions it would be done the following way using the main.js file:
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './registerServiceWorker';
import { mapGetters, mapMutations, mapActions } from 'vuex';
Vue.config.productionTip = false;
const app = new Vue({
router,
store,
render: function (h) { return h(App) },
methods: {
...mapMutations([
'showLoading',
]),
...mapActions([
'addNotification',
]),
},
}).$mount('#app');
export default app;
Current vue3 chat.vue file:
<template>
<div></div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'Chat',
data: function() {
return {
}
},
methods: {
},
computed: {
...mapGetters({
chatMessages: 'chatMessageList',
}),
}
}
</script>
Current vue3 store.js file:
import { createStore } from 'vuex'
export default createStore({
state: {
chatMessages: {
list: [
{ type: "a", message: "test" }
]
}
},
mutations: {
addMessage(state, { type, message }) {
state.chatMessages.list.push({ type: type, message: message });
}
},
actions: {
addMessage({ commit }, { type, message }) {
commit('addMessage', { type, message });
}
},
getters: {
chatMessageList(state, getters) {
return state.chatMessages.list;
}
}
})
Current vue3 main.js file:
import App from "./App.vue";
import {createApp} from "vue";
import router from "./router/index.js";
import store from "./store/store";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.js";
window.app = createApp(App).use(router).use(store).mount('#app');
EDIT: I tested it the following way and I can call app.addMessage from the dev console but now the router wont work.
import App from "./App.vue";
import {createApp} from "vue";
import router from "./router/index.js";
import store from "./store/store";
import { mapGetters, mapMutations, mapActions } from 'vuex';
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.js";
window.app = createApp({
methods: {
...mapActions([
'addMessage',
]),
}
}).use(router).use(store).mount('#app');
Assigning the store to the window object seems like a great approach, where you can easily then call it like in domiatorov's answer.
Another approach is:
var store = Array.from(document.querySelectorAll('*')).find(e => e.__vue_app__).__vue_app__.config.globalProperties.$store;
var actions = store._actions;
actions.addMessage[0]('mytype', 'mymessage');
The first part queries the body for an element containing __vue_app__ and will return your instance. In there, you can access config.globalProperties.$store to return your store object.
store is already available in this scope and can be exposed the same way as app:
window.store = store;
It can be used in console the same way as in an app:
store.dispatch(...)
I believe you can simply assign the store to the window object.
But do it from a top level single file component, the very first that is using the store and not in the setup to make sure the store got everything loaded.
Providing you have something like App.vue:
In setup() you would assign:
window.vueStore = store;
and use it from the console calling window.vueStore.

vuex unknown action type: RawHTML in a VueJS using typescript application

I am getting a '[vuex] unknown action type: RawHTML' when trying to dispatch an action on a component.
Those errors are usually caused by not correctly namespaced modules, but I am not using modules here.
store/index.ts
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const SETRAWHTML = ''
const store = new Vuex.Store({
state: {
raw:''
},
mutations: {
[SETRAWHTML](state,str){
state.raw=str
},
},
actions: {
RawHtml({commit}, str) {
commit(SETRAWHTML, str)
},
},
getters: {
getRawHTML (state) {
return state.raw
}
},
})
export default store;
main.ts
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
my component
click(){
store.dispatch('RawHTML', this.rawHTML)
}
Thanks in advance
The name of actions is case-sensitive and you should dispatch the action with the same name as it's defined in the store, RawHTML and RawHtml don't refer to the same action, so you should respect the case and write the action dispatch as follows :
click(){
store.dispatch('RawHtml', this.rawHTML)
}

vuex unknown action type: showRegisterLogin/show

I wrote the following code but it shows an error. What is the reason for this?
Error
[vuex] unknown action type: showRegisterLogin/show
HomePage.vue // component
When using the sh method This error is caused
import { mapState, mapActions } from "vuex";
export default {
name: "HomePage",
components: {
RegisterLogin
},
data() {
return {}
},
computed: {
...mapState({
showRegisterLogin: state => state.showRegisterLogin.show
}),
},
methods: {
sh() {
this.$store.dispatch('showRegisterLogin/show');
}
}
}
/ store / modules / showRegisterLogin.js
// States
const state = {
show: false,
};
// Getters
const getter = {
show (state) {
return state.show;
}
};
// Mutations
const mutation = {
showPage (state) {
return state.show = true;
},
hidePage (state) {
return state.show = false;
}
};
// Actions
const action = {
show({ commit }) {
commit('showPage');
},
hide({ commit }) {
commit('hidePage');
}
};
export default {
namespaced: true,
state,
getter,
mutation,
action
}
/ store / store.js
'use strict';
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
import showRegisterLogin from "./modules/showRegisterLogin";
export default new Vuex.Store({
modules: {
showRegisterLogin,
}
});
I also imported the store.js file into app.js and registered it in new vue
The structure of the store, module, and component are fine, except for the name of the store objects in your module:
getter should be getters
mutation should be mutations
action should be actions
Probably just typos. Those can't be arbitrarily named since Vuex looks specifically for those keys.

Trying unit testing store, How to make it work?

I have this store.js file
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const mutations = {
increment: state => state.count++,
Changeloading(state, status) { state.loading = status },
ChangeUserGroup(state, newGroup) { state.userGroup = newGroup; },
ChangeOriginalRole(state, newRole) { state.originalRole = newRole; }
}
export default new Vuex.Store({
state: {
count: 0,
loading: false, //header, TeamLead, TeamMember
listUserGroup: [],
userRole: "",
originalRole: "",
userGroup: {}
},
mutations,
...
})
In my testing file store.spec.js
import { expect } from 'chai'
import mutations from '#/store'
// destructure assign `mutations`
const { increment } = mutations
describe('mutations', () => {
it('INCREMENT', () => {
// mock state
const state = { count: 0 }
// apply mutation
increment(state)
// assert result
expect(state.count).to.equal(1)
})
})
This is the result i am getting:
mutations
1) INCREMENT
0 passing (75ms)
1 failing
1) mutations
INCREMENT:
TypeError: increment is not a function
at Context.increment (dist\webpack:\tests\unit\store.spec.js:13:5)
EDIT (4/16/2019)
I walk down one more step. I saw here that I should "export" all the components in my store.js like:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const mutations = {...};
export const state = {...};
export const actions = {...};
export const getters = {...};
export default new Vuex.Store({
state,
mutations,
actions,
getters
});
But even in that way... I'm getting the ugly (testing fail message)
0 passing (61ms)
1 failing
1) mutations
increment:
TypeError: increment is not a function
at Context.increment (dist\webpack:\tests\unit\store.spec.js:12:5)
I finally found the answer for this one:
In my test file I have
import { expect } from 'chai'
import mutations from '#/store'
right way to import mutations should be...
import { expect } from 'chai'
import {mutations} from '#/store'
The case was resolved :) by { }
Regards,

vuex: unknown getter: user

I'm experimenting with vuex and I was looking for best way to organize my vuex files I finished with something like this:
/src/store/user/state.js:
export default {
state: {
user: null
}
}
/src/store/user/getters.js:
export default {
getters: {
user (state) {
return state.user
}
}
}
/src/store/user/mutations.js:
export default {
mutations: {
'SET_USER' (state, user) {
state.user = user
}
}
}
/src/store/user/actions.js
export default {
actions: {
loginUser ({ commit }, params) {
commit('SET_USER', {id: 1})
}
}
}
/src/store/user/index.js
import state from './state'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'
export default {
state,
getters,
actions,
mutations
}
/src/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import user from './user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user
}
})
When I load my code it returns in console following error:
vuex: unknown getter: user
Each of your user-related files are using export default, which means when you import those files, you are naming the entire object being exported state, getters, etc.
So, in the scope of index, the state variable has a property named state, the getters variable has a property named getters, etc. And this is throwing things off.
You should export a const for each of these files instead:
export const state = {
user: null,
}
And then when importing grab the named const like so:
import { state } from './state'
Alternatively, you could just remove the properties for state, getters, etc. from each file:
// state.js
export default {
user: null,
}
And then import like you're doing already:
import state from './state'

Categories

Resources