Cannot access vuex mutation in dynamically registered module - vue.js

I have created a module to grab api data and hold it for a vue component:
import Vue from 'vue';
export default {
namespaced: true,
state() {
return {
all: []
}
},
getters: {
all(state) {
return state.all;
}
},
mutations: {
all(state, data) {
Vue.set(state, 'all', data);
},
clear(state) {
Vue.set(state, 'all', []);
}
},
actions: {
async init({ commit, state }) {
let response = await this.axios.get('/api/v1/teams/', {
params: {
includes: 'users'
}
});
if (response.status === 200) {
let data = response.data.data.filter(function (element) {
return element.users.length > 0;
});
commit('userTeamSelect/all', data, {root: true});
} else {
Vue.toasted.error('Could not fetch team data', {
duration: 10000
});
}
}
}
}
I have a component that is dynamically registering the module when it is created and removing the module from the store before it is deleted:
created() {
if (this.$store.getters['userTeamSelect/all'] === undefined) {
this.$store.registerModule('userTeamSelect', UserTeamSelectModule);
this.$store.dispatch('userTeamSelect/init');
}
},
beforeDestroy() {
if (this.$store.getters['userTeamSelect/all'] !== undefined) {
this.$store.commit('userTeamSelect/clear');
this.$store.unregisterModule('userTeamSelect');
}
},
When I navigate to a page where this component is used, I get the following error message in the console:
[vuex] unknown mutation type: userTeamSelect/all
This mutation is only called within the init action of the dynamic module, so the problem must be there somewhere. I have tried calling it both locally commit('all', data) and globally commit('userTeamSelect/all', data, {root:true}) with no luck.
The module must have been added to the vuex store because I can call the action without issue. It just seems the mutation isn't being registered somehow? I would expect it to work fine, or recieve an error about an unknown action.
Can anyone point me in the right direction please?

Related

Unabled to use Vuex Getter with params

I have Vuex Store that will look like this
const config = {
featureA: { isEnabled: true, maxUser: 2 },
featureB: { isEnabled: false, maxData: 5 },
}
const actions = {
getDataCompany(context, payload) {
return new Promise(async (resolve, reject) => {
try {
const result = await firebase.firestore().collection(payload.collection).doc(payload.companyId).get()
if (result) {
if (payload.isLogin) await context.commit('setConfig', result.data())
return resolve(result.data())
}
reject(new Error('Fail To Load'))
} catch (e) {
reject(new Error('Connection Error'))
}
})
}
}
const mutations = {
setConfig(state, payload) {
state.config = payload
}
}
const getters = {
getData: ({ config }) => (feature, key) => {
const state = config
if (state) if (state[feature]) if (state[feature][key]) return state[feature][key]
return null
}
}
export default new Vuex.Store({
state: { config },
actions: { ...actions },
mutations: { ...mutations },
getters: { ...getters }
})
It's working fine with this method to get the data
computed: {
featureAEnabled() {
return this.$store.getters.getData('featureA', 'isEnabled')
},
}
But I have a problem when the data is change, the value is not update in component, and now I want to use mapGetters because it say can detect changes, But I have problem with the documentation and cannot find how to pass params here,
import { mapGetters } from 'vuex'
computed: {
...mapGetters({
featureAEnabled: 'getData'
})
}
I'am calling the action from here
async beforeMount() {
await this.$store.dispatch('getDataCompany', {collection: 'faturelsit', companyId: 'asep', isLogin: true})
}
And try to detect change in here
mounted() {
if (this.featureAEnabled) console.log('feature enabled')
}
The value change is not detected, and need to refresh twice before the changes is implemented in component
My main target is to detect if there any data change in Vuex and make action in component,
nevermind just working with watch without mapgetter,
I just realize that computed cannot re-run the mounted, so I make method that will called when the variable change in watch. thank you.
The main purpose is fulfilled, but the mapgetter with params is still not answered. so if anyone want to answer please share the way to use mapgetter with params.
You could try to use get and set methods for your computed property.
Example:
computed: {
featureAEnabled: {
get() {
return this.$store.getters.getData('featureA', 'isEnabled')
},
set(value) {
...update featureEnabled property in vuex store
}
},
}

Vuex: Data sometimes gets 'undefined' during hard refresh

I am using Vuex for the first time and I have this occasional problem in console:
Error: TypeError: Cannot read property 'name' of undefined
Info: render
Any idea on how I can fix this problem?
Here is my setup:
In Vuex (store.js) I have a getter like so:
state: {
statuses: []
},
actions: {
async fetchStatuses({ commit }) {
try {
const response = await ApiService.getStatusFlags()
commit('SET_STATUSES', response.data)
} catch (err) {
console.error(err)
}
}
},
getters: {
getStatusById: state => id => {
return state.statuses.find(status => status.id === id)
},
...snip...
And on the page where I am calling this getter is like so:
IssueDetail.vue template:
<span class="badge badge-success">{{ getStatusById(issue.status).name }}</span>
IssueDetail.vue script section:
import { mapGetters } from 'vuex'
export default {
name: 'IssueDetail',
data() {
return {
isBusy: true,
issue_id: this.$route.params.id,
issue: ''
}
},
async created() {
await this.getIssue()
this.isBusy = false
},
methods: {
async getIssue() {
try {
this.issue = (await ApiService.getIssue(this.$route.params.id)).data[0]
} catch (error) {
console.error(error)
}
}
},
computed: {
...mapGetters(['getStatusById'])
}
this
getStatusById(issue.status).name
cannot work with your default value of the issue prop. Either change your default value or change getStatusById so it can accept null or undefined as an input param OR change your expression in a template to something like:
issue.status? getStatusById(issue.status).name : ''

How to check if JSON data is loaded

I use axios to fetch my JSON file en vuex for using the fetched data over multiple components.
The thing is that my page renders before all data is loaded.
The following works because I delayed the rendering by 2 seconds, without this timeout it would result in an error.
I would like to do this the proper way but am not sure how to do it.
STORE.JS
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
poss: null
},
getters: {
NAME: state => {
return state.name
},
POSS: state => {
return state.poss
}
},
mutations: {
SET_POSS : (state,payload) => {
state.poss = payload
},
ADD_POSS : (state,payload) => {
state.poss.push(payload)
},
},
actions:{
GET_POSS : async (context,payload) => {
let { data } = await axios.get("json/poss.json")
context.commit('SET_POSS',data)
},
SAVE_POSS : async (context,payload) => {
let { data } = await axios.post("json/poss.json")
context.commit('ADD_POSS',payload)
}
}
});
COMPONENT.VUE
module.exports = {
mounted:function(){
var self = this;
setTimeout(function () {
self.mkPageload()
}, 2000);
},
methods: {
mkPageload: function(){
let positions = this.$store.getters.POSS.pos
Object.keys(positions).forEach(key => {
// rendering
}
}
}
}
The desired result is that the page is only rendered after all data from the JSON file has been loaded.
There are several ways to solve this.
You could use wait / async in your component.
async mounted () {
await userStore.getAll()
// access getter and render
},
Your could watch vuex variable like (could be done without async but I like to add them)
async mounted () {
await userStore.getAll()
},
computed: {
...mapGetters('users')
},
watch: {
users(newValue, oldValue) {
....
// render
}
}
dont'forget to import the mapGetters: https://vuex.vuejs.org/guide/getters.html

Vue - Data not computed in time before mount

I'm learning Vue and I've run into a problem where my data returns undefined from a computed method. It seems that the data is not computed by the time the component is mounted, probably due to the get request - wrapping my this.render() in a setTimeout returns the data correctly. Setting a timeout is clearly not sensible so how should I be doing this for best practice?
Home.vue
export default {
created() {
this.$store.dispatch('retrievePost')
},
computed: {
posts() {
return this.$store.getters.getPosts
}
},
methods: {
render() {
console.log(this.comments)
}
},
mounted() {
setTimeout(() => {
this.render()
}, 2000);
},
}
store.js
export const store = new Vuex.Store({
state: {
posts: []
},
getters: {
getPosts (state) {
return state.posts
}
},
mutations: {
retrievePosts (state, comments) {
state.posts = posts
}
},
actions: {
retrievePosts (context) {
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token
axios.get('/posts')
.then(response => {
context.commit('retrievePosts', response.data)
})
.catch(error => {
console.log(error)
})
}
}
})
It is because axios request is still processing when Vue invokes mounted hook(these actions are independent of each other), so state.posts are undefined as expected.
If you want to do something when posts loaded use watch or better computed if it's possible:
export default {
created() {
this.$store.dispatch('retrievePost')
},
computed: {
posts() {
return this.$store.getters.getPosts
}
},
methods: {
render() {
console.log(this.comments)
}
},
watch: {
posts() { // or comments I dont see comments definition in vue object
this.render();
}
}
}
P.S. And don't use render word as methods name or something because Vue instance has render function and it can be a bit confusing.

Using methods on vuex getter

I am trying to do a .find() method on a Vuex getter.
However when I do so, it gives me the following error:
Uncaught TypeError: ur.find is not a function
I have followed the docs and I cannot seem to understand why I cannot do methods like .find() or .forEach() on the user-Object provided by the vuex Getter.
Am I doing something wrong, or is this type of thinking out of the scope?
This is an example of my mixin auth.js
export const auth = {
methods: {
y(r){
const ur = this.$store.getters.getUserRoles;
console.log(ur); // Returns {__ob__: Observer}...
ur.find( uRole => { // ur.find is not a function
return uRole === r
})
}
}
};
And this is my Vuex instance from the main vue; app.js
const store = new Vuex.Store({
state: {
user: {}
},
mutations: {
setUser (state, data) {
state.user = data;
},
},
getters: {
getUserRoles(state){
return state.user.roles;
}
},
actions: {
getUser ({commit}) {
axios.get('/api/user').then(response => {
const data = response.data.data;
commit('setUser', data);
});
}
}
});
The response from /api/user:
{
admin: true,
projectManager: false,
timeAdmin: false
}