Using NuxtJS (with TypeScript) I seem to have to specifically provide the store when I use getModule even though I have not defined my modules as dynamic. I cannot provide the store on the @module statement because I cannot import a store it seems - it isn't created at the point of the import.
This being so, I have to use getModule(MyModule, this.$store) in my pages/components.
So how do I now access (for example) a getter from ModuleB from an action in ModuleA?
I am expecting to do something like this:
ModuleA
import ModuleB from './moduleb`;
import { getModule } from 'vuex-module-decorators';
@Action
public async myAction(payload: IPayload): Promise<void> {
const moduleB = getModule(ModuleB, <I need the store here>);
const someModuleBData = moduleB.someModuleBGetter;
}
Where do I get the store from?
At the moment I am using this.context to access getters/mutations/actions from other modules but then my type safety goes out the window.
Same question: how best to access one module from a second module?
Otherwise: excellent project, MUCH better than vuex-class !
Seems like I also have to use this.context, see below.
Note the line this.context.rootState.first.posts.length in the second_module where it accesses state in the first_module, ie: this.context.rootState.<<other module>>.property
But: it would be great if the second module could use getModule() to get the first module.
1) First Module:
import { Module, VuexModule } from 'vuex-module-decorators'
@Module({name: 'first' })
export class FirstModule extends VuexModule {
posts: string[] = []
@Mutation addPost () {
this.posts.push(new Date().toString())
}
}
2) Second Module:
import { Module, VuexModule } from 'vuex-module-decorators'
@Module({name: 'second'})
export class SecondModule extends VuexModule {
get post_count(){
return this.context.rootState.first.posts.length // SECOND MODULE ACCESSES FIRST MODULE
}
}
3) store.ts
import { FirstModule } from '@/first_module'
import { SecondModule } from '@/second-module'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
modules: {
first : FirstModule,
second: SecondModule
}
})
3) component:
<template>
<div>
Book demo
<button @click="add_post">add post</button>
<div>posts</div>
<div v-for="p in posts">
{{p}}
</div>
<div>x: {{second_module.post_count}}</div>
</div>
</template>
<script lang="ts">
import { FirstModule } from '@/first_module'
import { SecondModule } from '@/second-module'
import { Component, Vue } from 'vue-property-decorator'
import { getModule } from 'vuex-module-decorators'
import { store } from './store-experiment'
@Component
export default class kDemo extends Vue {
first_module = getModule(FirstModule, store)
second_module = getModule(SecondModule, store)
posts = this.postsModule.posts
count = this.second_module.post_count
add_post() {
this.postsModule.addPost()
}
}
</script>
<style scoped>
</style>
@championswimmer
we can have a saveStore method which can save the store so that we don't have to pass the store to getModule everywhere or just anywhere..
We will save store in your library as soon as we construct our store (before any getModule is used).
@pranavjindal999
Sounds like a good idea. I will check the feasibility of that.
At the moment I am using this.context to access getters/mutations/actions from other modules but then my type safety goes out the window.
How do you access _action_ using this.context? I'm getting ERR_ACTION_ACCESS_UNDEFINED when calling this.context.dispatch('moduleB/foo', 123, { root: true }); from moduleA action.
UPDATE
I figured out why: you need to call getModule(MobuleB, this.$store) _anywhere_ in your app on initialization to make that work... Strange behavior... or I doing something wrong.
UPDATE 2
Nah, now I have another problem: if action return error (promise reject or just throw error) -ERR_ACTION_ACCESS_UNDEFINED again.
Why was this closed? Is there a solution that is typesafe now?
Yes indeed, is there any recommended solution to this problem?
See #124 for my solution.
Hi @danielroe
I had a quick look and it pretty much looks the same as the solution I documented here: https://github.com/championswimmer/vuex-module-decorators/issues/80
But what I am missing from both yours (and my) solutions is the answer to this specific question - how to access one module from another module in a typesafe way? It addresses access from a Vue component - but I can't see how it solves this. Can you elaborate.
By the way - no idea why this issues was closed without an answer
@kpturner Yes, it is just your solution (for which, huge thanks!), but made into a Vuex plugin rather than initialising in each component. This plugin-based approach works for me both accessing from other modules as well as from middleware, etc.
So, for example in one store I have this code:
import { secondStore } from '~/store'
@Module({
name: 'first',
stateFactory: true,
namespaced: true,
})
export default class FirstModule extends VuexModule {
sampleStoreProperty: string = []
get computedProperty() {
return secondStore.someGetter.find(thing => thing.id === 1)
}
}
Ahh - I see you have moved away from the classic mode (which annoyingly is being deprecated in Nuxt 3). Let me give that subtle change in tactics a go. Be back later.....
OK everything builds and I have intellisence for the module actions - but at runtime I just get
[vuex] unknown action type: <module>/<action>
Must be missing something fundamental here.....
So @danielroe in your example of the vuex in
dex.ts you define your plugin(s) like so
const initialiser = (store: Store<any>) => initialiseStores(store);
export const plugins = [initialiser];
(changed "z" to "s" :D )
So this is an unfamiliar way of defining plugs-ins for me - but I figure is is part of the non-classic mode way of doing things. Is there anything else to it though - because initialiseStores is not getting called at all in my app. Is there a specific minimum version of Vuex or Nuxt at play here?
@kpturner I'm not sure when the feature was included, but I see someone indicating they couldn't make it work in Dec 2018 - https://github.com/nuxt/nuxt.js/issues/4908 - and confirmation that Nuxt supports this methodology in Jan 2019 - https://github.com/nuxt/nuxt.js/issues/4908#issuecomment-459373511.
What version of Nuxt are you dealing with?
No I was barking up the wrong tree. It works fine for me in Nuxt 2.8 and Nuxt 1.4.4
The problem I was having was the fact that referencing modules this way seems to alter the namespacing. My modules were all in a subfolder of store - i.e. store/modules and the actions were no longer being resolved. The internal path changed from myModule/MyAction to module/MyModule/MyAction. I moved all the modules into the root of store and it all works well :)
So we do have a solution after all.
I am unable to make this work (I'm not using Nuxt)...
Would anyone have a working example to share...?
@danielroe Is there a way of getting your approach #124 to work with nested modules?
All examples that I have seen is just index.ts and then all modules one level up but what if i have:
store/company/offices.ts where "offices" is a nested module of the module "company"
You can see my working solution at https://github.com/garyo/vuex-module-decorators-example, as mentioned in #183 .
@garyo this will work for dynamic modules??
At the moment I am using
this.contextto access getters/mutations/actions from other modules but then my type safety goes out the window.
@kpturner How are u doing it??
I'm trying to call this mutation that is into Infoset module from another module: this.context.commit('Infoset/setSomethingInThisModule', {payload})
But the vuex is thinking this is a local mutation.
@aislanmaia If you are using the pattern I mention in the readme, you just do exactly the same from other modules.
import exampleStore from '~/store'
and then use it within the module as you wish.
You could also inject it into the Nuxt context within a plugin, and define the accessor pattern in your index.d.ts.
I tested a pattern here like this:
/* Somewhere in the end of the file module */
export default getModule(ModuleAStore)
export const ModuleA: BindingHelpers = namespace(name)
And now I'm capable to do something like this in another action from other module:
/* In some action from Module B, call the Module A mutations/actions/getters/state */
ModuleA.myMutation(payload)
}
It works like a charm!!!
That's is how I import the things and declare at the top of the file (for Module A, B, ...etc)
import store from '@/store/index.store'
import { namespace } from 'vuex-class'
import { BindingHelpers } from 'vuex-class/lib/bindings'
import {
getModule,
Module,
VuexModule,
Action,
Mutation
} from 'vuex-module-decorators'
const name: string = 'ModuleA'
@Module({ namespaced: true, store, name, dynamic: true })
class ModuleAStore extends VuexModule {
/* state */
/* getters */
/* actions */
/* mutations */
}
Why not something like this?
const rootState = this.context.rootState as RootStore;
rootState.role.removeAppRoles(id);
where RootStore is your store/index.ts interface
What worked for me was this:
this.context.dispatch('module/action', payload, {root: true});
Setting root to true you are specifying to dispatch an action from the root of the store. But there should be much better solutions.
Most helpful comment
Why was this closed? Is there a solution that is typesafe now?