Vuex how to pass state/getter in data prop - javascript

I retrieved my data from database using axios under my vuex
const state = {
giveaways: null
}
const actions = {
getGiveAways : ({commit}) =>{
axios({
url : '/prod/api/thresholds_settings',
method: 'post',
data : {
},
config: 'JOSN'
})
.then(response=>{
if(response.status == 200){
//console.log(response.data.total_giveaways);
commit('SET_GIVEAWAYS', response.data.total_giveaways)
}
})
.catch(error=>{
if(error.response){
console.log('something happened')
}
});
}
}
const mutations = {
SET_GIVEAWAYS : (state, obj)=>{
state.giveaways = obj
}
}
const getters = {
doneGiveAways(state){
return state.giveaways
}
}
In my Dashboard.vue I have
import {mapState,mapGetters} from 'vuex'
export default{
data: () => ({
cards: [],
giveaways: ''
}),
computed:{
...mapState({
Giveaway: state => state.Threshold.giveaways
}),
doneGiveAways(){
return this.$store.getters.doneGiveAways
}
},
ready(){
//giveaways: this.Giveaways
//console.log(this.Giveaways);
},
created(){
const self = this
this.cards[0].result_val = 2
this.cards[2].result_val = 2;
},
mounted(){
this.$store.dispatch('getGiveAways');
console.log(this.giveaways);
}
}
My problem is I need to pass the value from the mapState Giveaway to my returning data giveaways: '' so when page fires I can get the response value using this.giveaways. If I just call {{ Giveaway }} in my html it shows the value. But I need to make something like this.giveaways = this.$store.state.Thresholds.giveaways

I would use Stephan-v's recommendation and delete the local copy of giveaways. But I don't know what your specific reason is for declaring an extra copy of giveaways, so here is a solution that will work:
In your Dashboard.vue add:
export default {
...
watch: {
Giveaway(value) {
this.giveaways = value
}
},
...
}

Just delete the giveaways property from your data Object and rename the computed doneGiveAways to giveaways and you are done.
There is no need for a local component giveaway data property in this scenario.

Related

Pinia|Vue3 I can't access the property of the object that returned from the Pinia action

first of all I am using the Mockjs to simulate the backend data:
{
url: "/mockApi/system",
method: "get",
timeout: 500,
statusCode: 200,
response: { //
status: 200,
message: 'ok',
data: {
'onlineStatus|3': [{
'statusId': '#integer(1,3)',
'onlineStatusText': '#ctitle(3)',
'onlineStatusIcon': Random.image('20*20'),
'createTime': '#datetime'
}],
'websiteInfo': [{
'id|+1': 1,
}]
}
}
}
the data structure would be: https://imgur.com/a/7FqvVTK
and I retrieve this mock data in Pinia store:
import axios from "axios"
import { defineStore } from "pinia"
export const useSystem = defineStore('System', {
state: () => {
return {
systemConfig: {
onlineStatus: [],
},
}
},
actions: {
getSystemConfig() {
const axiosInstance = axios.interceptors.request.use(function (config) {
// Do something before request is sent
config.baseURL = '/mockApi'
return config
}, function (error) {
// Do something with request error
return Promise.reject(error);
})
axios.get('/system/').then(res => {
this.systemConfig.onlineStatus = res.data.data.onlineStatus
})
// console.log(res.data.data.onlineStatus)
axios.interceptors.request.eject(axiosInstance)
}
}
})
I use this store in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]
},
},
components: {UserOnlineStatus }
}
template in Profile.vue I import the child component userOnlineStatus.vue
<UserOnlineStatus :userCurrentOnlineStatus="userData.onlineStatus">
{{ showUserOnlineStatusText }}
</UserOnlineStatus>
here is what I have got https://imgur.com/fq33uL8
but I only want to get the onlineStatusText property of the returned object, so I change the computed code in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]['onlineStatusText']//👀I chage it here!
},
},
components: {UserOnlineStatus }
}
but I will get the error in the console and it doesn't work:
https://imgur.com/Gb68Slk
what should I do if I just want to display the specific propery of the retrived data?
I am out of my wits...
I have tried move the store function to the child components, but get the same result.
and I google this issue for two days, nothing found.
Maybe it's because of I was trying to read the value that the Profile.vue hasn't retrieved yet?
in this case, how could I make sure that I have got all the value ready before the page rendered in vue3? Or can I watch this specific property changed, then go on rendering the page?
every UX that has data is coming from remote source (async data) should has spinner or skeleton.
you can use the optional chaining for safe access (if no time to await):
return this.getUserOnlineStatus?.[this.getUserOnlineStatusIndex - 1]?.['onlineStatusText']

Vue, Vuex & remote storage

Well a have some value in remote storage (lets say x) and b-form-checkbox that should control this value. I want to inform user if value actually changed on storage and time when it happens.
So basically I want:
When user check/uncheck b-form-checkbox I want to change state of b-form-checkbox, send async request to the remote storage and show some b-spinner to indicate that state isn't actually changed yet.
When I receive answer from remote storage:
if change was successful just hide b-spinner.
if change was not successful (timeouted, error on server, etc) I want to change b-form-checkbox state back (since value actually doesn't changed on storage) and hide b-spinner
What is the silliest way to do int using Vue + Vuex?
Currently I'm doing it this way:
xChanger.vue:
<template>
<b-form-checkbox v-model="xComp" switch>
{{xComp ? 'On' : 'Off'}}
<b-spinner v-if="!xSynced"/>
</b-form-checkbox>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
export default {
name: 'XChanger',
computed: {
...mapState(['x']),
...mapGetters(['xSynced']),
xComp: {
get() { return x.local },
set(value) {
if (value != this.x.local) {
this.setX(value)
}
},
},
},
methods: {
...mapActions(['setX']),
},
}
</script>
main.js
import Vuex from 'vuex'
import Axios from 'axios'
const store = new Vuex.Store({
state: {
x: {
remote: null,
local: null
},
getters: {
xSynced(state) {
state.x.local === state.x.remote
}
},
actions: {
async setX(store, value) {
store.state.x.local = value
try {
let response = await Axios.post('http://blah.blah/setX', {x: value});
if (response.status == 200) {
store.state.x.remote = value
}
} catch (error) {
store.state.x.local = !value
}
}
},
mutations: {
setX(state, value) {
state.x.local = value
state.x.remote = value
}
}
},
})
But it is too verbose for just one value to be controlled (especially computed property xComp). I'm sure that such a simple template should be already solved and has more simple way to implement.
Here is an example:
<template>
<b-form-checkbox v-model="x.local" switch>
{{x.local ? 'On' : 'Off'}}
<b-spinner v-if="saving"/>
</b-form-checkbox>
</template>
<script>
export default
{
name: 'XChanger',
data: () => ({
x:
{
local: false,
remote: false,
},
saving: false,
}),
watch:
{
'x.local'(newValue, oldValue)
{
if (newValue !== oldValue && newValue !== this.x.remote)
{
this.updateRemote(newValue);
}
}
}
methods:
{
async updateRemote(value)
{
try
{
this.saving = true;
const response = await Axios.post('http://blah.blah/setX', {x: value});
if (response.status == 200)
{
this.x.remote = value;
}
else
{
this.x.local = this.x.remote;
}
}
catch (error)
{
this.x.local = this.x.remote;
}
this.saving = false;
}
},
}
</script>

Vuex getters has undefined data when I try to load data from API and use it in multiple components

I have a page component where I am making api call and storing the data in Vuex store through actions. This data has to be used at multiple places but everywhere I'm initially getting undefined data which loads after a few seconds asynchronously from the API. How should I use vuex getters asynchronously ?
Here's the code for my vuex store module :
import axios from 'axios';
const state = {
all_pokemon: {},
pokemon_details: {}
};
const getters = {
getAllPokemon: function(state) {
return state.all_pokemon;
},
getPokemonDetails: function(state) {
return state.pokemon_details;
}
};
const mutations = {
setAllPokemon: function(state, payload) {
return state.all_pokemon = payload;
},
setPokemon: function(state, payload) {
console.log('Pokemon details set with payload ', payload);
return state.pokemon_details = payload;
}
};
const actions = {
setPokemonAction: function({ commit }, passed_pokemon) {
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
console.log('Response data is : ', response.data);
});
commit('setAllPokemon', response.data);
},
setPokemonDetailAction: function({ commit }, passed_pokemon) {
console.log('Action method called..', passed_pokemon);
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
commit('setPokemon', response.data);
});
}
};
export default {
state,
getters,
mutations,
actions,
};
And code for the component where I want to get this data and pass it to other components :
<script>
import { mapGetters, mapActions } from 'vuex'
import axios from 'axios'
// Imported other components here
export default {
name: 'pokemon_detail_page',
data() {
return {
current_pokemon: this.$route.params.pokemon,
isLoading: false,
childDataLoaded: false,
search_pokemon: '',
sprites: [],
childData: 'False',
isAdded: false,
pokemon_added: 'none_display',
show: false
}
},
methods: {
...mapActions([
'setPokemonDetailAction',
'removePokemon'
]),
},
computed: {
...mapGetters([
'getPokemonDetails',
'getTeam'
])
},
components: {
Game_index,
PulseLoader,
PokemonComponent,
},
filters: {
},
watch: {
getTeam: function (val) {
},
getPokemonDetails: function(val) {
}
},
created() {
setTimeout(() => {
this.show = true;
}, 2000);
this.$store.dispatch('setPokemonDetailAction', this.current_pokemon)
.then(() => {
// const { abilities, name, order, species, } = {...this.getPokemonDetails};
})
},
mounted() {
},
}
</script>
And here's is the code for the template where I'm passing this data to multiple components :
<div v-if="show" class="pokemon_stats_container" :key="childData">
<ability-component
:abilities="getPokemonDetails.abilities"
>
</ability-component>
<sprites-component
:sprites="sprites"
>
</sprites-component>
<location-component
:location_area="getPokemonDetails.location_area_encounters"
:id="getPokemonDetails.id"
>
</location-component>
<stats-component
:stats="getPokemonDetails.stats"
>
</stats-component>
<game_index
:game_indices="getPokemonDetails.game_indices"
/>
<moves-component
:moves="getPokemonDetails.moves"
:pokemon_name="getPokemonDetails.name"
>
</moves-component>
</div>
As of now, I've adopted a roundabout way of doing this through setTimeout and setting a variable after 2 seconds so that data is available for other components to use. But, there has to be a more elegant way of handling vuex asynchronous data. Someone please help me in this.
Your first commit is not in the promise
commit('setAllPokemon', response.data);
make this :
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
console.log('Response data is : ', response.data);
commit('setAllPokemon', response.data);
});
try to use in your vue component
$forceUpdate()
when your request is end for reload the data

How to prevent Vue from loading the data multiple times?

I'm currently practicing Vue with Vuex and the REST API architecture (with Django). I wanna retrieve the data from by database and store the data in my global state in order to access it throughout my Vue components. It works fine so far but there is one weird thing I do not understand.
When I start my application I have the homepage where currently nothing is displayed. I click on the menu item "Contacts" and the contacts component loads (router-view) (API GET call is executed) and displays the table of all the created contacts and shows it properly (source of truth is now my global state). I can edit, delete and view a contact.The contacts are now stored in the global state as well.
The problem: Everytime I load the contact component the mounted() lifecycle gets called (which makes sense) and loads the contacts from the API inside the state so the whole lists gets dupblicated over and over. I just want Vue to make a GET request only once and then access the state's data (where to contacts are stored now).
Another scenario is when I update a contact and click back to the contacts menu item the list contains the old contact and the updated one but when I refresh the page it is fine.
Thanks!
MY CODE
state.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
import { apiService } from "../utils/api.service.js";
export const store = new Vuex.Store({
state: {
contacts: []
},
mutations: {
initContacts_MU: (state, data) => {
state.contacts.push(...data);
},
updateContact_MU: (state, data) => {
let getContact = state.contacts.filter(contact => contact.id === data.id);
let getContactIndex = state.contacts.indexOf(getContact);
state.contacts.splice(getContactIndex, 1, data);
},
deleteContact_MU: (state, data) => {
let getContact = state.contacts.filter(contact => contact.id === data.id);
let getContactIndex = state.contacts.indexOf(getContact);
state.contacts.splice(getContactIndex, 1);
},
createContact_MU: (state, data) => {
state.contacts.unshift(data);
}
},
actions: {
initContacts_AC({ commit }) {
let endpoint = "api/contacts/";
apiService(endpoint).then(data => {
commit("initContacts_MU", data);
});
},
updateContact_AC({ commit }, contact) {
let endpoint = "/api/contacts/" + contact.slug + "/";
apiService(endpoint, "PUT", contact).then(contact => {
commit("updateContact_MU", contact);
});
},
deleteContact_AC({ commit }, contact) {
let endpoint = "/api/contacts/" + contact.slug + "/";
apiService(endpoint, "DELETE", contact).then(contact => {
commit("deleteContact_MU", contact);
});
},
createContact_AC({ commit }, contact) {
let endpoint = "/api/contacts/";
apiService(endpoint, "POST", contact).then(contact => {
commit("createContact_MU", contact);
});
}
},
getters: {
contacts: state => {
return state.contacts;
}
}
});
ContactList.vue
<script>
import Contact from "../../components/Contacts/Contact.vue";
import { mapGetters, mapActions } from "vuex";
export default {
name: "ContactList",
components: {
Contact
},
computed: {
...mapGetters(["contacts"])
},
methods: {
...mapActions(["initContacts_AC"])
},
mounted() {
this.initContacts_AC();
}
};
</script>
Just check if there're contacts which are already retrieved from backend.
computed: {
...mapGetters(["contacts"])
},
methods: {
...mapActions(["initContacts_AC"])
},
mounted() {
if (this.contacts && this.contacts.length > 0) return; // already fetched.
this.initContacts_AC();
}
EDIT:
updateContact_MU: (state, data) => {
const contactIndex = state.contacts.findIndex(contact => contact.id === data.id);
if (contactIndex < 0) return;
state.contacts.splice(contactIndex, 1, data);
}

Vue.js: mutation for deleting a comment

I have been working on the feature of comment deleting and came across a question regarding a mutation for an action.
Here is my client:
delete_post_comment({post_id, comment_id} = {}) {
// DELETE /api/posts/:post_id/comments/:id
return this._delete_request({
path: document.apiBasicUrl + '/posts/' + post_id + '/comments/' + comment_id,
});
}
Here is my store:
import Client from '../client/client';
import ClientAlert from '../client/client_alert';
import S_Helper from '../helpers/store_helper';
const state = {
comment: {
id: 0,
body: '',
deleted: false,
},
comments: [],
};
const actions = {
deletePostComment({ params }) {
// DELETE /api/posts/:post_id/comments/:id
document.client
.delete_post_comment({ params })
.then(ca => {
S_Helper.cmt_data(ca, 'delete_comment', this);
})
.catch(error => {
ClientAlert.std_fail_with_err(error);
});
},
};
delete_comment(context, id) {
context.comment = comment.map(comment => {
if (!!comment.id && comment.id === id) {
comment.deleted = true;
comment.body = '';
}
});
},
};
export default {
state,
actions,
mutations,
getters,
};
I am not quite sure if I wrote my mutation correctly. So far, when I am calling the action via on-click inside the component, nothing is happening.
Guessing you are using vuex the flow should be:
according to this flow, on the component template
#click="buttonAction(someParams)"
vm instance, methods object:
buttonAction(someParams) {
this.$store.dispatch('triggerActionMethod', { 'something_else': someParams })
}
vuex actions - Use actions for the logic, ajax call ecc.
triggerActionMethod: ({commit}, params) => {
commit('SOME_TRANSATION_NAME', params)
}
vuex mutations - Use mutation to make the changes into your state
'SOME_TRANSATION_NAME' (state, data) { state.SOME_ARG = data }

Categories

Resources