I'm following this tutorial about Vuex Pagination (https://whatthecode.dev/easy-vuejs-vuex-pagination/?utm_source=rss&utm_medium=rss&utm_campaign=easy-vuejs-vuex-pagination)
The only difference is that I changed my API request, however I can retrieve frontend data from state, but can't trigger action. I'm new to VueX, can someone spot the mistake?
PS: It never reaches console.log("Let's get")
store.js
import Vue from 'vue';
import Vuex from 'vuex';
import volumes, { VOLUMES_MODULE } from './volumes';
Vue.use(Vuex);
const store = new Vuex.Store ({
modules: {
[VOLUMES_MODULE]: volumes,
},
});
export default store;
volumes/index.js
import state from './state';
import actions from './actions';
export const VOLUMES_MODULE = 'volumes'
export default {
namespaced: true,
actions,
state,
}
export * from './state'
volumes/actions.js
import VolumeService from '../../services/VolumeService';
import {
SET_DATA,
SET_PAGINATION,
} from './mutations'
import state from './state';
export const FETCH_VOLUMES = 'load_volumes'
const volumeService = new VolumeService();
export default {
async [FETCH_VOLUMES]({ commit }, payload) {
console.log("Let Get");
const volumes = await volumeService.getTwentyVolumes({
...state.pagination,
...payload,
})
commit(SET_DATA, volumes.data)
commit(SET_PAGINATION, {
page: 2,
limit: 20,
totalPages: 2,
})
},
}
volumes/mutations.js
export const SET_PAGINATION = 'set_pagination'
export const SET_DATA = 'set_data'
export default {
[SET_PAGINATION](state, pagination) {
state.pagination = pagination
},
[SET_DATA](state, data) {
state.data = data
},
}
volumes/state.js
export const VOLUMES = 'data'
export const PAGINATION = 'pagination'
export default {
[VOLUMES]: [],
[PAGINATION]: {
page: 1,
limit: 20,
totalPages: 1,
},
}
First check if you have installed store to your view, normally in the main.js where you new a Vue instance, code should be like this:
import Vue from "vue";
import App from "./App.vue";
import store from "./store";
Vue.config.productionTip = false;
new Vue({
store,
render: (h) => h(App)
}).$mount("#app");
Then in the component where you want to call the load_volumes, use mapActions to add this function. code sample:
<script>
import {mapActions} from 'vuex'
export default {
name: "App",
components: {
},
methods: {
...mapActions({load_volumes: 'volumes/load_volumes'})
},
mounted(){
this.load_volumes()
}
}
</script>
working example can be found here:
https://codesandbox.io/s/call-vuex-actions-in-components-msdyo
Related
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.
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)
}
In spite of my understanding that NUXT does namespacing automatically. Because of this, I am unable to test or reference the store in any of my testing modules. Can anyone give me a tip? Maybe where I can edit the namespacing property in a Nuxt app?
Here is the code below for the component, store, and the test.
ButtonComponent.vue:
<template>
<v-container>
<v-btn #buttonClick v-model="value"></v-btn>
</v-container>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
data: {
return {
value: 25
}
}
methods: {
buttonClick(event) {
this.$store.dispatch('buttonComponent/setNewValue', valuePassedIn)
},
},
}
</script>
<style scoped></style>
buttonComponent.spec.js:
import Component from '../../Component'
import { mount, createLocalVue } from '#vue/test-utils'
import expect from 'expect'
import Vue from 'vue'
import Vuex from 'vuex'
import Vuetify from 'vuetify'
const localVue = createLocalVue()
localVue.use(Vuex)
Vue.use(Vuetify)
describe('Component', () => {
let store
let vuetify
let actions
beforeEach(() => {
actions = {
actionClick: jest.fn()
}
store = new Vuex.Store({
actions,
})
vuetify = new Vuetify()
})
it('method sends value to store when button is clicked', async () => {
const wrapper = mount(Component, {
store,
localVue,
vuetify,
})
wrapper.find('.v-btn').trigger('click')
expect(actions.actionClick).toHaveBeenCalledWith('buttonComponent/setNewValue', 25)
})
})
buttonComponent.js:
export const state = () => ({
value: 0,
})
export const mutations = {
SET_TO_NEW_VALUE(state, value) {
state.value = value
},
}
export const actions = {
setNewValue({ commit }, value) {
commit('SET_TO_NEW_VALUE', value)
},
}
Just so that I don't have to write it again here, I'll link you to an article I just posted that walks through the setup process to so you can test your Nuxt stores with Jest: https://medium.com/#brandonaaskov/how-to-test-nuxt-stores-with-jest-9a5d55d54b28
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,
I have defined a variable called $shopifyClient within Vue which is not accessible in Vuex. How do i make thi variable accessible?
Vue.$shopifyClient.addLineItems('1234', lineItems).then((checkout) => {
console.log(checkout.lineItems)
})
returns TypeError: Cannot read property 'addLineItems' of undefined, so i would assume it cannot retrieve $shopifyClient.
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.prototype.$shopifyClient = new Client(
new Config({
domain: 'some-page.myshopify.com',
storefrontAccessToken: '123456'
})
)
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
template: '<App/>',
components: { App }
})
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
lineItems: { }
},
actions: {
addToCart ({ commit, state }) {
var lineItems = [{variantId: '12345==', quantity: 2}]
Vue.$shopifyClient.addLineItems('1234', lineItems).then((checkout) => {
console.log(checkout.lineItems)
})
}
}
})
You can declare $shopifyClient in Vuex store as shown below:
//Store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
lineItems: { },
$shopifyClient: new Client(
new Config({
domain: 'some-page.myshopify.com',
storefrontAccessToken: '123456'
})
)
},
actions: {
addToCart ({ commit, state }) {
var lineItems = [{variantId: '12345==', quantity: 2}]
state.$shopifyClient.addLineItems('1234', lineItems).then((checkout) => {
console.log(checkout.lineItems)
})
}
}
})
// vue component
//you can access it like below
this.$root.$store.state.$shopifyClient;