Setting value to input field using Vuex store modules - vue.js

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.

Related

How to trigger a Vue method when computed property changes

Essentially, in a Vue application, I have a page that lists Contacts. And a search form that is part of a header component. I capture the input search term as a state variable in the vuex store and track it as a computed variable.
I have a method to retrieve contacts that is called within the mounted hook. I want to call the same getContacts method again whenever the state search variable changes. Using a watcher to do this atm, but I have read that watching a computed value is not the right way. Wondering if there is a better way to do this. Script below.
<script>
import API from '#/utils/Api.js'
...
export default ({
...
tableData: [],
}
},
computed: {
search() {
return this.$store.getters.search;
}
},
mounted() {
this.getContacts();
},
methods: {
async getContacts() {
try {
const {data} = await API.get('/contacts?search=' + this.search)
this.tableData = data
} catch (e) {
console.error(e)
}
}
},
watch: {
search () {
this.getContacts();
}
}
})
</script>
Try to watch directly the state item and then pass the new value as parameter for the method, then use immediate:true to replace the mounted hook call :
export default ({
...
tableData: [],
}
},
methods: {
async getContacts(val) {
try {
const {data} = await API.get('/contacts?search=' + val)
this.tableData = data
} catch (e) {
console.error(e)
}
}
},
watch: {
'$store.getters.search': {
handler(newVal)
this.getContacts(newVal);
},
immediate:true
}
}
})

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.

Vuex action payload is undefined

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');

Correct way to cache data in vuex

I am trying to asynchronously load data into vuex that is static but is used by multiple routes.I only want to fetch the data once and only when a route that needs it is visited. This is what I'm currently doing but I'm not sure if this is the correct convention or if there is a better/more Vueish way.
// store.js
export default new Vuex.Store({
state: {
_myData: null,
},
getters: {
myData: (state) => new Promise((resolve,reject) => {
if(state._myData){
resolve(state._myData);
} else {
axios.get('http://myData.com/')
.then(myData => {
state._myData = myData;
resolve(state._myData);
});
}
})
}
});
// ProfilePage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
data() {
return {
myData:null
}
},
async created(){
this.myData = await this.$store.getters.myData;
}
}
</script>
// AboutPage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
data() {
return {
myData:null
}
},
async created(){
this.myData = await this.$store.getters.myData;
}
}
</script>
There is a correct way to do what you want but it is not the way you are doing it. Vue is quite strict on "Do not mutate vuex store state outside mutation handlers."
This means you should only alter the store state through a mutation, then use your getter only to get the data. You should also use an action to commit the mutation. So for what you are trying to do you should try it like this.
// AnyPage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
data() {
return {
myData:null
}
},
async created(){
if(this.$store.state._myData === null) {
await this.$store.dispatch('getData')
}
this.myData = this.$store.getters.myData;
}
}
</script>
then in your store:
// store.js
export default new Vuex.Store({
state: {
_myData: null,
},
getters: {
myData: (state) => state._myData,
}
mutations: {
SET_DATA(state, data) {
state._myData = data
}
}
actions: {
getData({ context }){
axios.get('http://myData.com/')
.then(res => {
context.commit('SET_DATA', res)
})
}
}
}
});
You should read up in the docs which covers it all pretty well.
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.
https://vuex.vuejs.org/guide/actions.html
try this:
// AnyPage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
computed: {
myData () {
return this.$store.state.myData
}
},
mounted () {
this.$store.dispatch('getData')
}
}
</script>
in store file:
// store.js
export default new Vuex.Store({
state: {
myData: null,
},
mutations: {
SET_DATA(state, data) {
state.myData = data
}
}
actions: {
getData({ context }){
if (context.state.myData === null) {
axios.get('http://myData.com/')
.then(res => {
context.commit('SET_DATA', res)
})
}
}
}
}
});