Commit mutation in a different Vuex module - javascript

I got the following structure:
store.js
|
|-- yields.js
|
|--analysis (sub-folder)
|
|
---actions.js
|
---mutations.js
|
---state.js
In mutations.js I set the date of a state in state.js like:
stateFetchParamsStart: (state, input) => {
state.fetchParams.start = input;
// Want to change yields.PRdate too
//state.yields.PRdate = input;
}
How can I access the PRdate state of yields.js with the mutation from state.js?
EDIT with more information:
I'm setting initial values of PRdate when the component is mounted like:
mounted(){
...
this.fetchPRData(this.pvSystem.system_id); // makes an axios call
}
Also I've set a computed property asking for the date:
getPrDate: {
get(){
return this.$store.state.yields.PRdate;
}
}
Now when clicking on a date picker I want the date to change (also computed propery on a v-model):
start: {
get() {
return this.$store.state.analysis.fetchParams.start
},
set(value) {
this.$store.commit('stateFetchParamsStart', value)
}
}

You can change a Vuex module's state from another module, but it has to be done through an action. That action can call a mutation in the other module. So first, create a mutation in your yields module:
mutations: {
statePRdate(state, input) {
state.PRdate = input;
}
}
Now you can use an action in the analysis module to commit that mutation. Pass a third argument to the commit call:
{ root: true }
The first argument will be the mutationName or moduleName/mutationName if your modules are namespaced. Here is the new action below that calls both a mutation in its own module and a mutation in another module:
actions: {
analysisAction({ commit }, input) {
commit('stateFetchParamsStart', input); // Commit in this module
commit('statePRdate', input, { root: true }); // Commit in another module
}
}
Initiate the action like:
this.$store.dispatch('analysisAction', 'input')
Namespaced modules
If your modules are namespaced, then you'd use namespaced syntax for both the commit and the action:
commit('yields/statePRdate', input, { root: true })
this.$store.dispatch('analysis/analysisAction', 'input')

Related

Vue FireStore Reactive Key Assignment - Object is possibly 'undefined'

I am attempting to assign FireStore data(), forwarded by props, to a reactive() proxy object, but am receiving the following error:
Object is possibly 'undefined'.
(property) fireStoreData: Record<string, any> | undefined
I wish to use a forEach loop instead of direct assignments i.e. (pageForm.name = props.fireStoreData?.name) to minimize code.
props: {
fireStoreData: Object,
}
...
setup(props){
...
const pageForm: { [key: string]: any } = reactive({
name: '',
address: '',
...
})
onMounted(() => {
Object.keys(pageForm).forEach(key => {
if(key in props.fireStoreData) // <-- error found here
pageForm[key] = props.fireStoreData[key] // <-- and here
})
})
})
...
}
The issie is that the fireStoreData prop is not required yet in your code you assume it is.
try:
props: {
fireStoreData: {
required: true,
type: Object
}
}
If you dont want the prop to be required, its always possible to check for it being defined and add a watcher to look for changes.
Also, dont forget props can change in value.
// on mobile, will format later on pc

Use ES6 classes as vuex store state in Nuxt

When I set ES6 class to state on my vuex store in nuxt I got following warn:
WARN Cannot stringify arbitrary non-POJOs EndPoint
and When I use object as state is works without warning.
So How can I use ES6 classes in my state.
My model:
export default class EndPoint {
constructor(newEndPoints) {
this.login = newEndPoints.login;
this.status = newEndPoints.status;
}
}
and mutate state here:
commit(CoreMutations.SET_ENDPOINTS, new EndPoint(response.data));
Using object:
const EndPoint = {
endPoints(newEndPoints) {
return {
login: newEndPoints.login,
status: newEndPoints.status
};
}
};
and mutate:
commit(CoreMutations.SET_ENDPOINTS, EndPoint.endPoints(response.data));
As discussion in comment add toJSON method in class solve the problem when setting state in client, but when set state in server, states will become undefined.
so adding this code solve the problem:
toJSON() {
return Object.getOwnPropertyNames(this).reduce((a, b) => {
a[b] = this[b];
return a;
}, {});
}

Vue/Vuex - Module two depends on module one, and module one gets data from server

Check this out:
import accountModule from '#/store/modules/account/account';
import otherModule from '#/store/modules/other/other';
export default new Vuex.Store({
modules: {
account: accountModule,
other: otherModule,
}
});
The data initialization in other depends on the account module because the account module has user specific settings. Suppose other.state.list depends on account.state.settings.listOrder. However, I want the data for the account module to come from the server. Which is async. So when other is trying to get set up, it can't just try to reference account.state.settings.listOrder because the response from the server may not have come back yet.
I tried exporting a promise in accountModule that resolves with the module itself. But that approach doesn't seem to work.
import accountModulePromise from '#/store/modules/account/account';
accountModulePromise.then(function (accountMoudle) {
import otherModule from '#/store/modules/other/other';
...
});
This gives me an error saying that import statements need to be top level.
The following doesn't work either:
let accountModule = await import '#/store/modules/account/account';
import otherModule from '#/store/modules/other/other';
...
It gives me an error saying that await is a reserved word. I'm confused though, because https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import says that I should be able to do it.
Your last code block didn't work because of await have to be inside async function.
Remember, the await keyword is only valid inside async functions. If
you use it outside of an async function's body, you will get a
SyntaxError.
From MDN.
You can use Dynamic Module Registration:
accountModulePromise.then(async () => {
let otherModule = await import('#/store/modules/other/other');
store.registerModule('other', otherModule.default);
});
But when you want to get state or dispatch actions you have to check whether module is registered which is pretty bad.
In my opinion it would be better if you redesign your module structure to decoupling each other. Try to move your initialize code to main.js or App.vue then dispatch actions to update module states from that.
Updates
From your last update, Another idea to decoupling your store, I think you should store your list without order and sort it only when you use. You can do this with:
Computed property:
...
computed: {
list () {
let list = this.$store.state.other.list
let order = this.$store.state.account.settings.listOrder
if (!list || !order) return []
return someSort(list, order)
}
},
beforeCreate () {
this.$store.dispatch('other/fetchList')
this.$store.dispatch('account/fetchListOrder')
}
...
Or Vuex getters:
...
getters: {
list: (state) => (order) => {
return someSort(state.list, order)
}
}
...
...
computed: {
list () {
let order = this.$store.state.account.settings.listOrder
return this.$store.getters['others/list'](order)
}
}
...
Okay, so you have two modules. One with state that is fetched from the server, the other with state that is dependent on the first, correct?
I would suggest the following approach:
Set up your modules with empty 'state' to begin with. Then create an action within accountModule to set up the state from the server. Use a getter on other to order the list. Finally, dispatch your action upon app creation.
const account = {
namespaced: true,
state: {
listOrder: ''
},
mutations: {
setListOrder (state, newListOrder) {
state.listOrder = newListOrder
}
},
actions: {
async fetchServerState (ctx) {
let result = await fetch("/path/to/server")
ctx.commit('setListOrder', result.listOrder)
// or whatever your response is, this is an example
}
}
}
const other = {
namespaced: true,
state: {
unorderedList: []
},
getters: {
list (state, getters, rootState) {
return someSort(state.unorderedList, rootState.account.listOrder);
}
}
}
within App.vue (or wherever)
created () {
this.$store.dispatch('account/fetchServerState')
}

How to use Vue plugin in Store?

Is there a proper / documented way of using a plugin inside vuex module or plain js module?
I am using event bus to acheive it, not sure if it is the correct / best way. Please help.
Plugin1.plugin.js:
const Plugin1 = {
install(Vue, options) {
Vue.mixin({
methods: {
plugin1method(key, placeholderValues = []) {
return key;
},
},
});
},
};
export default Plugin1;
In App.vue:
Vue.use(Plugin1, { messages: this.plugin1data });
In store / plain-js module:
const vue = new Vue();
const plugin1method = vue.plugin1method;
you can access your Vue instance using this._vm;
and the Vue global using import Vue from 'vue'; and then Vue;
I'm guessing you defined an instance method, so it would be the former (this._vm.plugin1method())
update
I can't tell you which way you should use it because it I can't see how your function is defined in your plugin.
However, here is an example that should illustrate the difference between instance and global
const myPlugin = {
install: function(Vue, options) {
// 1. add global method or property
Vue.myGlobalMethod = function() {
// something logic ...
console.log("run myGlobalMethod");
};
Vue.mixin({
methods: {
plugin1method(key, placeholderValues = []) {
console.log("run mixin method");
return key;
}
}
});
// 4. add an instance method
Vue.prototype.$myMethod = function(methodOptions) {
console.log("run MyMethod");
// something logic ...
};
}
};
Vue.use(Vuex);
Vue.use(myPlugin);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
this._vm.$myMethod();
Vue.myGlobalMethod();
this._vm.$options.methods.plugin1method(); // <-- plugin mixin custom method
state.count++;
}
}
});
when you commit the increment ie: this.$store.commit('increment') both methods will execute

Vuex: Call getters from action

Is there a way for a dispatch/action to call a getter inside of it?
mutations: {
setData(state, data) {
state.data = data;
}
}
actions: {
sendDataToServer({ commit }, payload) {
// call getter (data) and assign to variable
// do async functions from the data returned
}
},
getters: {
getAppData: state => () => {
return state.data;
}
}
So what's the best practice here? Using the mutation to change the state and then get the state and pass it to action which will then execute the async function or do I need to restructure my implementation?
call mutation -> get the data via getter -> call action
OR
do it all on the action (do mutation on the action and do the action/async method without the need of the getter)?
In addition to commit, actions has default injected parameters which are dispatch, getters and rootGetters. So you can simply write;
sendDataToServer({ commit, getters }, payload) to access getters.
You have access to getters inside an action:
getters: {
getUser(state){
return state.user
}
}
actions : {
myAction({ getters }){
let user = getters.getUser
}
}
In the action, you see the first parameter has {commit} in it. Similarly, you can pass {commit, state}. This way, you can directly access the state.data.
I think in your example, you would want to do the action because you can call the mutation from inside action itself using commit('setData').
The first parameter is there for you to use state and mutation as you prefer. Personally, I have only worked on projects where you do the action first and do mutation to store it in the app. For example, if I want to store a car info in the server somewhere, first I would do the action (and save it to remote db). Once I confirm that it saved in db, I would locally mutate in the store. This totally depends on case by case basis. But good thing is that you can mutate from inside the action
Action handlers receive a context object which exposes the same set of methods/properties on the store instance, so you can call context.commit to commit a mutation, or access the state and getters via context.state and context.getters
actions: {
sendDataToServer(context, payload) {
// context object contains state, commit, getters
context.getters.getAppData
}
},
Refer docs: https://vuex.vuejs.org/guide/actions.html#dispatching-actions
If you are using nuxt and isolated files in vuex, like this =
store -
|
|-- index.js
|
|-- store.js
|
|-- product.js
// store.js
export const getters = {
getNameStore: state => state.getNameStore ? state.getNameStore : null
};
I want the getNameStore of the store.js into product.js
// product.js
export const actions = {
setResultSearch({ commit, dispatch }, text) {
console.log(
'getNameStore',
this.getters["store/getNameStore"]
);
};
this.getters["store/getNameStore"]

Categories