Vuex action payload is undefined - vue.js

I have a component that looks like this(simplified):
<script>
import { mapActions } from 'vuex';
import router from '#/router';
import { bindingService } from '#/_services/binding.service';
export default {
props: {
serialNumber: { type: String, default: ' ' }
},
data: () => ({
subscriptions: ['Loading...'],
vin: null,
}),
computed: {
splittedSerialNumber() {
return this.serialNumber.split(' ')[0];
}
},
created() {
//fetch some data
bindingService.fetchSomeData('xxx').then((data) => {
this.vin = data;
});
},
methods: {
...mapActions('binding', ['setDeviceSerialNumber', 'setVehicleIdentificationNumber']),
cancel() {
router.push('/home');
},
ok() {
console.log(this.vin); //this console.log outputs valid data
this.setVehicleIdentificationNumber(this.vin);
},
},
};
</script>
Then I have my store that look like this(simplified):
const state = {
vehicleIdentificationNumber: null,
};
const actions = {
setVehicleIdentificationNumber({ commit }, { vin }) {
console.log(vin); // this console.log outputs undefined
commit('SET_VEHICLE_IDENTIFICATION_NUMBER', vin);
}
};
const mutations = {
SET_VEHICLE_IDENTIFICATION_NUMBER(state, vin) {
state.vehicleIdentificationNumber = vin;
},
};
const binding = {
namespaced: true,
state,
actions,
mutations,
};
export default binding;
I'm even more confused because I've been using pretty much the same format of actions and mutations in this project and they work.
I'm out of ideas and looking forward to any kind of input :)

In your setVehicleIdentificationNumber method on the component, you are passing in the vin as an integer argument.
In the action, the param is an object: { vin }.
Change the action param to vin, or pass in an object in the component: { vin: this.vin }

I think the problem here is that your vin property isn't reactive because you initialized it with a null value, but you're changing it to an object. Try this:
bindingService.fetchSomeData('xxx').then((data) => {
Vue.set(this, 'vin', data)
});
Of course, you'll need to import Vue from 'vue'

You should pass the data to the action like this:
actions: {
myAction( store, payload = {myCustomKey: 'my value 1'} ){
store.commit('myCustomMutation', payload.myCustomKey);
}
}
And later уоu can call the action with or without the data:
this.$store.dispatch('myAction');
this.$store.dispatch('myAction', 'my value 2');

Related

Vue - Composable and emit - Passing function from parent to grandchild

I am new to Vue and I have recently started to use Composition api.
I understand that emit in Option Api but I am not really sure what is the benefit in Composition Api...
For example, in OptionApi:
Parent.vue
export default {
name: 'Parent',
Methods: {
sayHi (name) {
console.log('Hi!' + name);
},
},
};
Child.vue
// Pass parent method to GrandChild.vue
GradeChild.vue
export default {
name: 'GradeChild',
Methods: {
emitToChild () {
this.$emit('childCompSayHi', this.name);
},
},
};
In Composition Api:
Parent.vue
export default {
name: 'Parent',
setup() {
const { name } = useName();
const sayHi = () => {
console.log('Hi!' + name.value);
};
return {
sayHi,
};
},
};
GrandChild.vue
export default {
name: 'GradeChild',
setup() {
const { setName } = useName();
setName(name);
},
};
useName.js
const name = ref('');
export const useColorPicker = () => {
const setName = (newName) => {
name.value = newName;
};
return {
name,
setName,
};
};
Correct me if I am wrong but Composable can do the same thing without emit, right?
If we have a parent component who passes a function down to a grandchild to get grandchild's data, we can just use composable and create a setter function and use it in the grandchild and then use the variable in the parent component without having to pass the function all components down, no?

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
}
},
}

How to get value of Vuex getter inside of Vue local data object

Hey this seemed to be pretty simple, but I just can't figure out how to get my loggedInUser object to work outside the template. I read through a bunch of other answers and I know I have to do something to store the getter in a local data object.
I have a temporary 'id' object set up because that's what I ultimately want to set to loggedInUser.id in order to attach to my Axios request.
Here's the page I want to make changes on:
<script>
import { mapGetters } from 'vuex'
// import vuex from 'vuex'
export default {
data: () => ({
results: "",
id: "15",
}),
computed: {
...mapGetters(['loggedInUser'])
},
// var id = {{loggedInUser}};
methods: {
getData() {
this.$axios.get('http://127.0.0.1:8000/api/v1/actors/',
{params: {user: this.id} }
)
.then(response => {this.results = response.data});
}
}
}
</script>
and here's my index.js for store:
export const getters = {
isAuthenticated(state) {
return state.auth.loggedIn
},
loggedInUser(state) {
return state.auth.user
}
}
The mapGetters helper simply maps store getters to local computed
properties
This:
computed: {
...mapGetters(['loggedInUser'])
},
Is equivalent to this:
computed: {
loggedInUser() {
return this.$store.getters.loggedInUser
},
},
Replace this.id with this.loggedInUser.id in your axios request, and get rid of the temporary id data property.
getData() {
this.$axios.get('http://127.0.0.1:8000/api/v1/actors/', {
params: {
user: this.loggedInUser.id
}
})
.then(response => {
this.results = response.data
});
}
We are assuming getData only gets called when the user is authenticated. If that's not the case, have in mind this.loggedInUser may be null or undefined and accessing this.loggedInUser.id will throw an error.

how to get nested getters in vuex nuxt

i have store/index.js like this
new Vuex.Store({
modules: {
nav: {
namespaced: true,
modules: {
message: {
namespaced: true,
state: {
count: 0,
conversations: [],
},
getters: {
getCount: state => {
return state.count;
},
},
mutations: {
updateCount(state) {
state.count++;
},
},
actions: {},
},
requests: {
namespaced: true,
state: {
friends: [],
},
getters: {
getFriends: state => {
return state.friends;
},
},
mutations: {
pushFriends(state, data) {
state.friends.push(data);
},
},
actions: {
pushFriends(commit, data) {
commit('pushFriends', data);
},
},
},
},
},
},
});
i want to use getters in computed property i have tested like this
computed: {
...mapGetters({
count: 'nav/message/getCount',
}),
},
butt getting error
[vuex] unknown getter: nav/message/getCount
what is am missing here
i also want to make separate folder for every modules like my nav have 3 modules message, requests & notifications
i did try but nuxt blow up my codes
I think your index is wrong, the correct thing is to separate the modules independently, something like this:
in your store/index.js
export const state = () => ({
config: {
apiURL: 'https://meuapp.com'
}
})
export const mutations = { }
export const actions = { }
// getters
export const getters = {
test: state => payload => {
if (!payload)
return {
message: 'this is an messagem from index without payload test.', // you don't need pass any payload is only to show you how to do.
result: state.config
}
else
// return value
return {
message: 'this is an message from index test with payload.',
result: state.config, // here is your index state config value
payload: payload // here is yours params that you need to manipulate inside getter
}
}
}
here is your store/navi.js
export const state = () => ({
navi: {
options: ['aaa', 'bbb', 'ccc']
}
})
export const mutations = { }
export const actions = { }
// getters
export const getters = {
test: state => payload => {
if (!payload)
return {
message: 'this is a messagem from nav store without payload test.', // you don't need pass any payload is only to show you how to do.
result: state.navi
}
else
// return value
return {
message: 'this is an messagem from navi test with payload.',
result: state.navi, // here is your index state config value
payload: payload // here is yours params that you need to manipulate inside getter
}
}
}
then in your component you can use as a computed properties:
<template>
<div>
without a paylod from index<br>
<pre v-text="indexTest()" />
with a paylod from index<br>
<pre v-text="indexTest( {name: 'name', other: 'other'})" />
without a paylod from navi<br>
<pre v-text="naviTest()" />
with a paylod from navi<br>
<pre v-text="naviTest( {name: 'name', other: 'other'})" />
access getters from methods<br>
<pre>{{ accessGetters('index') }}</pre>
<pre v-text="accessGetters('navi')" />
<br><br>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters({
indexTest: 'test',
naviTest: 'navi/test'
})
},
methods: {
accessGetters (test) {
if (test && test === 'index' ) {
console.log('test is', test) // eslint-disable-line no-console
return this.indexTest()
}
else if (test && test === 'navi') {
console.log('test is:', test) // eslint-disable-line no-console
return this.naviTest()
}
else {
return 'test is false'
}
}
}
}
</script>
Whenever possible separate your code into smaller parts, one part for each thing. This makes it easier for you to update and keep everything in order.
Hope this helps.
I came here to find a way to access the getters of a module that was nested inside another module in Vue.js and the following solution worked for me:
this.$store.getters['outerModuleName/innerModuleName/nameOfTheGetter']
Maybe this helps someone with a similar problem.

Setting value to input field using Vuex store modules

I have a vuex in module mode that fetching the data of a user:
store/modules/users.js
import axios from "axios";
export const state = () => ({
user: {}
});
// Sets the values of data in states
export const mutations = {
SET_USER(state, user) {
state.user = user;
}
};
export const actions = {
fetchUser({ commit }, id) {
console.log(`Fetching User with ID: ${id}`);
return axios.get(`${process.env.BASE_URL}/users/${id}`)
.then(response => {
commit("SET_USER", response.data.data.result);
})
.catch(err => {
console.log(err);
});
}
};
// retrieves the data from the state
export const getters = {
getUser(state) {
return state.user;
}
};
then on my template pages/users/_id/index.vue
<b-form-input v-model="name" type="text"></b-form-input>
export default {
data() {
return {
name: ""
}
},
created() {
// fetch user from API
this.$store.dispatch("fetchUser", this.$route.params.id);
}
}
Now I check the getters I have object getUser and I can see the attribute. How can I assign the name value from vuex getters to the input field?
watcher is probably what you need
export default {
// ...
watch: {
'$store.getters.getUser'(user) {
this.name = user.name;
},
},
}
While Jacob's answer isn't necessarily incorrect, it's better practice to use a computed property instead. You can read about that here
computed: {
user(){
return this.$store.getters.getUser
}
}
Then access name via {{user.name}} or create a name computed property
computed: {
name(){
return this.$store.getters.getUser.name
}
}
Edit: fiddle as example https://jsfiddle.net/uy47cdnw/
Edit2: Please not that if you want to mutate object via that input field, you should use the link Jacob provided.