Vuex access modules in subdirectories - vue.js

I have the following directory structure:
store
modules
file1.js
file2.js
file3.js
anotherFolder
filex1.js
My question is, how to access the state of anotherFolder/filex1.js
I can set the value, but I can't get it.
this.$store.dispatch('anotherFolder/filex1/myAction', {}) //work
let val = this.$store.state.anotherFolder.filex1.myValue //not work
I am importing the modules as follows.
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)
const modulesFiles = require.context('./modules', true, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters
})
export default store
my filex1.js
export default {
namespaced: true,
state: {
myValue: {}
},
actions: {
myAction({ commit }, reg) {
commit('MY_ACTION', reg)
}
},
mutations: {
MY_ACTION(state, reg) {
state.myValue = reg
}
}
}
I want to organize the store's files in subdirectories so I don't have everything in the modules folder.
thanks!

you could use the binding helpers provided by vuex in your component to access the state in a nested module:
computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
})
}
see:
https://vuex.vuejs.org/guide/modules.html#namespacing

Related

How to pass an argument to Pinia store?

I'm making a session API call in main.js and using values from the response as the initial value for my root store. In vuex it's handled this like,
DataService.getSession()
.then((sessionData) => {
new Vue({
i18n,
router,
// this params sessionData.session will be passed to my root store
store: store(sessionData.session),
render: (h) => h(App),
}).$mount('#app');
})
Consumed like,
export default function store(sessionData) { // here I'm getting the sessionData
return new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
state: {
// some states here
},
});
}
In case of Pinia we're creating a app instance & making it use like,
app.use(createPinia())
And my store would be like,
// how to get that sessionData here
import { defineStore } from 'pinia'
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({
counter: 0
})
})
Is it possible to pass the sessionData someway to the pinia store?
There are 3 ways to pass parameters to a Pinia store - see the list below. You could use either #2 or #3 .
In most cases it is wise to initialise your Vue instance and show the user something while they are waiting for connections to resolve. So you may find it simpler to just access or initialise the store by calling DataService.getSession() in say a "SessionStore" action which can be async. Typically Components =access=> Stores =access=> Services.
Unlike Vuex, you don't need a root Pinia store. You can get just call useSomeStore() in the setup method for any component. Each store can just be an island of data. Pinia stores can reference other pinia store instances. This might be particularly useful if you're migrating a set of Vuex stores to Pinia and need to preserve the old Vuex tree of stores.
1. Pass common params to every action.
export const useStore = defineStore('store1', {
state: () => ({
...
}),
actions: {
action1(param1: string ... ) {
// use param1
}
}
});
2. Initialise store AFTER creating it
Only works if there's one instance of this store required
export const useStepStore = defineStore('store2', {
state: () => ({
param1: undefined | String,
param2: undefined | String,
...
}),
getters: {
getStuff() { return this.param1 + this.param2; }
}
actions: {
init(param1: string, param2: string) {
this.param1 = param1
this.param2 = param2
},
doStuff() {
// use this.param1
}
}
});
3. Use the factory pattern to dynamically create store instances
// export factory function
export function createSomeStore(storeId: string, param1: string ...) {
return defineStore(storeId, () => {
// Use param1 anywhere
})()
}
// Export store instances that can be shared between components ...
export const useAlphaStore = createSomeStore('alpha', 'value1');
export const useBetaStore = createSomeStore('beta', 'value2');
You could cache the session data in your store, and initialize the store's data with that:
In your store, export a function that receives the session data as an argument and returns createPinia() (a Vue plugin). Cache the session data in a module variable to be used later when defining the store.
Define a store that initializes its state to the session data cached above.
In main.js, pass the session data to the function created in step 1, and install the plugin with app.use().
// store.js
import { createPinia, defineStore } from 'pinia'
1️⃣
let initData = null
export const createStore = initStoreData => {
initData = { ...initStoreData }
return createPinia()
}
export const useUserStore = defineStore('users', {
state: () => initData, 2️⃣
})
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { createStore } from './store'
import * as DataService from './data-service'
DataService.getSession().then(sessionData => {
createApp(App)
.use(createStore(sessionData)) 3️⃣
.mount('#app')
})
demo
When you create a store in Pinia using defineStore() you give it the initial state. So wherever you do that just pass the data into it and then do
defineStore('name', {
state: () => {
isAdmin: session.isAdmin,
someConstant: 17
},
actions: { ... }
});

Why vuex return object instead of array in Quasar App?

I have Quasar App
I connected vuex
I created a separate file as module for vuex like this:
I created an empty array in vuex
If I open Vue extension in browser I see that vuex return object where an array is stored instead of just an array
How can I store just an array instead of object?
I created a vuex file like this:
in store/index.js file I imported cars.js file:
import Vue from 'vue'
import Vuex from 'vuex'
import cars from "src/store/module-example/cars";
Vue.use(Vuex)
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
cars
},
// enable strict mode (adds overhead!)
// for dev mode only
strict: process.env.DEBUGGING
})
return Store
}
Cars.js file:
import axios from "axios";
const state = {
cars: []
}
const mutations = {
SET_CARS: (state, cars) => {
state.cars = cars;
}
}
const actions = {
async GET_ALL_CARS_FROM_API({commit}) {
let result = await axios.get('http://localhost:8050/api/cars');
commit("SET_CARS", result.data)
}
}
const getters = {
GET_ALL_CARS: (state) => {
return state.cars;
}
}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}
result.data is an object. In order to set cars to a plain array just set it to result.data.cars
commit("SET_CARS", result.data.cars)

how to get access to vuex inside other js files like plugins in nuxt

i have a problem. i wanna get access to one of my getters inside my vee-validate.js file.
how can i do that?
in my pages and components, (in <script>...</script> part, outside export default{...}) i used this function:
component.vue
<script>
let lang;
function getLang({store}) {
lang = store.state.lang
}
export default{
...
}
but it is not working!
i'm trying to access my custom lang file (for translation purpose) that is stored in lang state in my vuex, and use it in vee-validate.js file for custom message.
i tried to import store but not working.
veevalidate.js:
import Vue from 'vue'
import { required } from 'vee-validate/dist/rules'
import { extend, ValidationObserver, ValidationProvider, setInteractionMode } from 'vee-validate'
import {store} from '../store'
let langFile = store
setInteractionMode('eager')
extend('required', {
...required,
message: ''
})
Vue.component('ValidationProvider', ValidationProvider);
Vue.component("ValidationObserver", ValidationObserver);
UPDATED: My store index.js
import langfile from '../static/lang'
export const state = () => ({
lang: null,
dir: null,
})
export const getters = {
//----------------- Language and Direction
lang(state){
return state.lang
},
dir(state){
return state.dir
},
}
export const mutations = {
SET_LANGUAGE(state, lang){
state.lang = lang
},
SET_DIRECTION(state, dir){
state.dir = dir
},
}
export const actions = {
async nuxtServerInit({dispatch, commit}) {
// ------------- Read Language File
let baseLang = process.env.SITE_LANGUAGE;
let siteLang = langfile[baseLang];
let siteDir = langfile[baseLang]['dir'];
commit('SET_LANGUAGE', siteLang);
commit('SET_DIRECTION', siteDir);
},
}

Why am I getting "rawModule is undefined" when adding Vuex modules?

I recently was struggling with implementing modules in Vuex for the first time. I couldn't find much info on the console error message I was getting ( rawModule is undefined ), so I thought I'd share the issue I ran into and the solution. I was doing a quick, simple version of a module implementation as I was working through some examples:
export const store = new Vuex.Store({
state: {
loggedIn: false,
user: {},
destination: ''
},
mutations: {
login: state => state.loggedIn = true,
logout: state => state.loggedIn = false,
updateUser: ( state, user ) => { state.user = user },
updateDestination: ( state, newPath ) => { state.destination = newPath }
},
modules: {
project
},
});
const project = {
state: {}
}
The issue ultimately was that I had declared my module after I tried to add it to the Vuex store. I had thought it would have been okay to declare the module later thanks to variable hoisting, but that doesn't appear to be the case. Here is the code that does work:
const project = {
state: {}
}
export const store = new Vuex.Store({
state: {
loggedIn: false,
user: {},
destination: ''
},
mutations: {
login: state => state.loggedIn = true,
logout: state => state.loggedIn = false,
updateUser: ( state, user ) => { state.user = user },
updateDestination: ( state, newPath ) => { state.destination = newPath }
},
modules: {
project
},
});
Hopefully this saves some people some time. I didn't see anything in the documentation requiring a certain ordering, so I'm surprised it mattered. If anyone has some insight into why it works this way, I'd be really interested in hearing it! Perhaps because the Vuex.Store() function gets called before the project value is set, so the project module's value is encapsulated as undefined, and that causes the error?
If you have using class components, put the store import before the module import, Eg:
// This is wrong
import { getModule } from "vuex-module-decorators";
import AppModule from "#/store/app-module";
import store from "#/store";
const appModule = getModule(AppState, store);
// This work
import { getModule } from "vuex-module-decorators";
import store from "#/store"; // Before first
import AppModule from "#/store/app-module"; // Then import the module
const appModule = getModule(AppState, store);

How can I use Vuex modules state without nested objects?

I'm trying to convert existing Vuex state to modules. I'm trying to use modules like this:
const moduleA = {
state: {},
mutations: {
update (state, payload) { // payload is an object
state = payload // doesn't work
}
},
I'm registering modules:
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
The question is: is it possible to use state without nesting as I'm trying to do? Or the only way is to have something like:
const moduleA = {
state: {
one: {}
},
mutations: {
update (state, payload) {
state.one = payload // it works
},
},
}
Ideally, I'd like to have the same structure as it was before and being able to get the state as state.a instead of state.a.one
Thank you
Yes you can -- just refactor the modules into separate files and then use an index file to bring them all together:
import Vue from 'vue'
import Vuex from 'vuex'
import users from './store.users.js'
import entries from './store.entries.js'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
users,
entries
}
})
export default store
Then in each module's file (e.g. store.entries.js, you can follow the pattern you're familiar with, but with namespaced set to true:
const module = {
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {}
}
module.actions.update = ({ commit, state }, payload) => {
// commit, state, etc refer to the local store
}
export default module
For files using the store, you can import the index and then call by module:
import store from '../stores/store.index.js'
// ...
store.dispatch('entries/update', payload)
You can use state = Object.assign(state, payload). But note that this means you keep all existing props and shallow merge new ones in.
You cannot create a new state object unless you lift the logic up to the global namespace and use dynamic module registration