How do I use Vue.set() properly? - vue.js

I am trying to make an array within my store reactive.
I have currently tried using :key to force update, $forceUpdate() and Vue.set(). I originally was getting and updating the data within the calendar component, but I moved the get data logic to the store in hopes that somehow it would make it reactive. The current attribute shows a red dot on the prescribed v-calendar date. From what I can tell the array is populating with objects with the exact same structure as the single attribute, but it is not reactive.
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
loading: true,
odata: [],
attributes: [{
dates: new Date(),
dot: 'red',
customdata: {
programEventsSystemrecordID: 1234
}
}]
},
mutations: {
updateAtts (state) {
let singleAtt = {}
let index = 0
state.odata.forEach((ticket) => {
Vue.set(singleAtt, 'dot', 'red')
Vue.set(singleAtt, 'dates', new Date(ticket.ProgramEventsStartdate))
Vue.set(singleAtt, 'customData', {})
singleAtt.customData = {
programEventsSystemrecordID: ticket.ProgramEventsSystemrecordID
}
Vue.set(state.attributes, index, singleAtt)
index++
})
},
updateOdata (state, odata) {
state.odata = odata
},
changeLoadingState (state, loading) {
state.loading = loading
}
},
actions: {
loadData ({ commit }) {
axios.get('https://blackbaud-odata-cal-bizcswpdjy.now.sh')
.then((response) => {
commit('updateOdata', response.data)
})
.catch((err) => {
console.log(err)
})
.finally(() => {
console.log(commit('updateAtts'))
commit('changeLoadingState', false)
})
}
}
})
I expect the array that is being populated within vue to update the DOM. There are no error messages.

Vue.set is useless in your case. In mostly all cases, it's useless.
It's needed to add new properties in the state that where not initially.
Here, you just have one state property that is build from another.
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
loading: true,
odata: [],
attributes: [{
dates: new Date(),
dot: 'red',
customdata: {
programEventsSystemrecordID: 1234
}
}]
},
mutations: {
updateAtts (state) {
state.attributes = state.odata.map(t=>({
dot: 'red',
dates: new Date(t.ProgramEventsStartdate),
customData: {programEventsSystemrecordID: t.ProgramEventsSystemrecordID}
}))
},
updateOdata (state, odata) {
state.odata = odata
},
changeLoadingState (state, loading) {
state.loading = loading
}
},
actions: {
loadData ({ commit }) {
axios.get('https://blackbaud-odata-cal-bizcswpdjy.now.sh')
.then((response) => {
commit('updateOdata', response.data)
})
.catch((err) => {
console.log(err)
})
.finally(() => {
console.log(commit('updateAtts'))
commit('changeLoadingState', false)
})
}
}
})

Related

On component created hook call Action to fetch data from database and store it in state and then call Getter to get the data

So basically I have this component and I am using its created hook to fetch data using vue-resource and VUEX action, storing that data in store and right after that trying to get that data using VUEX getter but I am unable to do so. Any work around or I am doing something wrong. I am new to Vue!
Component:
import { mapActions } from 'vuex';
import { mapGetters } from 'vuex';
export default {
components: {
categoryHeader: CategoryHeader,
categoryFooter: CategoryFooter,
AddCategory
},
data() {
return {
openCatAdd: false,
categories: [],
pagination: []
}
},
methods: {
...mapActions([
'getCategories'
]),
...mapGetters([
'allCategories'
])
},
created() {
this.getCategories(1);
this.categories = this.allCategories();
// console.log(this.categories);
}
};
Store:
import Vue from "vue";
const state = {
categories: [],
};
const mutations = {
setCategories: (state, payload) => {
state.categories = payload;
}
};
const actions = {
getCategories: ({commit}, payload) => {
Vue.http.get('categories?page='+payload)
.then(response => {
return response.json();
})
.then(data => {
commit('setCategories', data.data);
}, error => {
console.log(error);
})
}
}
const getters = {
allCategories: state => {
console.log(state.categories);
return state.categories;
}
};
export default {
state,
mutations,
actions,
getters
};

How to Mock a store with global variable

I have a file that I'm using to store a global variable that gets changed by 'login' or 'logout' functions. I want to write a unit test that has the value of 'isLoggedIn' set to true or false, then checks for expected behaviour. I can't figure out what I need to do to be able to use the value, this is my file:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
loggedIn: false,
},
mutations: {
login(state) {
state.loggedIn = true;
},
logout(state) {
state.loggedIn = false;
state.userID = null;
},
},
actions: {
login({ commit }) {
commit('login');
},
logout({ commit }) {
commit('logout');
},
},
getters: {
isLoggedIn: (state) => state.loggedIn,
},
});
And this is the test I'm trying to create:
import { expect } from 'chai';
import { shallowMount } from '#vue/test-utils';
import Home from '#/views/images.vue';
describe('Images.vue', () => {
it('shows that you are logged in', () => {
const welcome_text = 'You are logged in.';
this.$store.dispatch('login');
const wrapper = shallowMount(Home, {});
expect(wrapper.text()).to.include(welcome_text);
});
});
Your getter method isn't returning anything.
https://vuex.vuejs.org/guide/getters.html#property-style-access
Once you change your getter to:
getters: {
isLoggedIn: (state) => return state.loggedIn,
},
You should be able to retrieve this value using:
this.$store.getters.isLoggedIn

Is there a way to detect query changes using Vue-Router and successfully get new data?

I am simply making an asynchronous request to get data about a MLB player but am failing to get new data by manually changing the query parameters in the URL. When I use watch, the from and the to are the same for some reason upon debugging with Vue dev tools. However, all works well when I manually click a link to navigate routes as the from and the to correctly represent the from and the to routes.
PitcherForm.vue
export default {
name: "PitcherForm",
components: {
PlayerForm,
},
watch: {
$route() {
this.handleSubmit({ firstName: this.$route.query.firstName, lastName: this.$route.query.lastName });
}
},
methods: {
handleSubmit: function(formValues) {
// this.$store.dispatch("fetchPlayer", { formValues, router: this.$router, player: "pitching" });
this.$store
.dispatch("fetchPlayer", { formValues, player: "pitching" })
.then((promiseObject) => {
console.log(promiseObject)
this.$router.push({
// name: 'PitcherData',
path: "pitching/player",
query: {
firstName: promiseObject.firstName,
lastName: promiseObject.lastName,
},
});
})
.catch((error) => {
console.log(error);
});
},
},
//store.js
import Vue from 'vue';
import Vuex from 'vuex';
import VuexPersist from 'vuex-persist';
import axios from 'axios';
Vue.use(Vuex);
// VuexPersist stuff
const vuexLocalStorage = new VuexPersist({
key: 'vuex',
storage: window.localStorage,
});
export const store = new Vuex.Store({
plugins: [vuexLocalStorage.plugin],
state: {
playerStats: []
},
// mutations, getters, excluded for convenience
actions: {
fetchPlayer({ commit }, data) {
return new Promise((resolve) => {
let firstName = data.formValues.firstName.replace(/\s/g, "").toLowerCase();
let lastName = data.formValues.lastName.replace(/\s/g, "").toLowerCase();
axios.get(`http://localhost:5000/${data.player}/player`,
{
params: {
first: firstName.charAt(0).toUpperCase() + firstName.slice(1),
last: lastName.charAt(0).toUpperCase() + lastName.slice(1),
},
}).then(response => {
commit('setPlayers', response.data);
}).catch((error) => {
console.log(error);
});
resolve({firstName, lastName});
});
}
}
});

How to display objects from VueX using fetched data from an API

I'm trying to experiment on displaying data using VueX and a free API from rapidapi. Somehow I can't display or iterate through it properly in the component.
The console displays the objects correctly, but the component that's supposed to display it does not.
What am I doing wrong?
Here are the relevant codes:
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
worldData:
fetch("https://covid-193.p.rapidapi.com/statistics", {
method: "GET",
headers: {
"x-rapidapi-host": "covid-193.p.rapidapi.com",
"x-rapidapi-key": "mySecretKey"
}
})
.then(response => response.json())
.then(data => {
data.response.sort((a, b) => (a.country > b.country ? 1 : -1));
console.log(data.response);
return data.response;
})
},
getters: {
worldData: state => state.worldData,
},
mutations: {
},
actions: {
},
modules: {
}
})
components/mycomponent.vue
<template>
<div >
<div v-for="myData in $store.getters.worldData" :key="myData">{{myData}}</div>
</div>
</template>
When you create a store, the state property is for initial / default values. You are currently setting yours to a Promise which is probably not what you want.
Performing asynchronous tasks should be done via actions and the results committed through mutations.
const store = new Vuex.Store({
state: {
worldData: [] // initial value
},
getters: {
worldData: state => state.worldData
},
mutations: {
setWorldData: (state, worldData) => state.worldData = worldData
},
actions: {
loadWorldData: async ({ commit }) => {
// load the data via fetch
const res = await fetch('https://covid-193.p.rapidapi.com/statistics', {
headers: {
"x-rapidapi-host": "covid-193.p.rapidapi.com",
"x-rapidapi-key": "mySecretKey"
}
})
// check for a successful response
if (!res.ok) {
throw res
}
// parse the JSON response
const worldData = (await res.json()).response
// commit the new value via the "setWorldData" mutation
commit('setWorldData', worldData.sort((a, b) => a.country.localeCompare(b.country)))
}
}
})
store.dispatch('loadWorldData') // dispatch the action to load async data
export default store
You can execute the dispatch anywhere at any time to load / reload the data.

Correct way to do a redirect after posting through axios in a vuex store

I am using nuxtjs, axios and vuex to post from a form component to post my data to my backend.
When posted I'd like to redirect to the view record screen and populate it with the returned information using the ID to navigate there
so my path might be /cases/14325 (if 14325 is the id returned once created)
What is the correct way to do this please
I have the following code in my vuex store
export const state = () => ({
cases: []
})
// *** MUTATIONS ***
export const mutations = {
add(state, newCase ) {
state.cases.push(newCase)
},
}
// *** ACTIONS ***
export const actions = {
addCase(context, newCase) {
const createdCase = {
...newCase
}
axios.post("http", createdCase)
.then(result => {
context.commit('add', {...createdCase, id: result.data.name})
})
.catch(e => console.log(e));
},
}
In my component I have the following
import { mapMutations, mapGetters, mapActions } from 'vuex'
export default {
data () {
return {
newCase: {
caseName: '',
summary: '',
status: 'live',
},
}
},
methods: {
...mapActions([
'addCase'
]),
onSubmit() {
// Save the post
this.$store.dispatch('addCase').then(path => {
this.$router.redirect(path)
}).catch((err) => {
console.log(err)
})
},
}
}
</script>
How do i return the new id from my store please and replace cases/1 with '/cases/' + new id?
Thanks for the help as always
Maybe is will be enough when you improve your action this way:
addCase(context, newCase) {
return new Promise ((resolve, reject) => {
const createdCase = {...newCase}
axios.post('http', createdCase).then(result => {
context.commit('add', {...createdCase, id: result.data.name})
resolve(/*path*/)
}).catch(e => {
console.log(e)
reject(/*reason*/)
})
})
}
And then you use it this way:
this.$store.dispatch('addCase', context).then(path => {
this.$router.redirect(path)
})