Given the following functioning vuex action named init that fetches a settings and an accounts collection:
actions: {
init: firestoreAction(({ bindFirestoreRef, commit }, payload) => {
bindFirestoreRef(
'settings', fb.settings.doc(payload),
)
.then(() => commit('SETTINGS_READY', true))
.catch((err) => {
commit('SNACKBAR_TEXT', err.message);
Sentry.captureException(err);
});
bindFirestoreRef(
'accounts', fb.accounts.where('program', '==', payload),
)
.then(() => commit('ACCOUNTS_READY', true))
.catch((err) => {
commit('SNACKBAR_TEXT', err.message);
Sentry.captureException(err);
});
}),
},
I have two questions:
The code appears to run synchronously, but I want the two collections to be fetched asynchronously to maximize performance. How can that be achieved?
Is it possible to refactor this code to be more concise and yet provide for the independent (and synchronous) then/catch functionality present in the example?
You can use async / await function then call the bindFirestoreRef inside Promise constructor.
actions: {
init: firestoreAction(async ({ bindFirestoreRef, commit }, payload) => {
await Promise((resolve, reject) => {
bindFirestoreRef('settings', fb.settings.doc(payload))
.then((res) => {
commit('SETTINGS_READY', true);
resolve(res);
})
.catch((err) => {
commit('SNACKBAR_TEXT', err.message)
Sentry.captureException(err)
reject(err)
})
})
await new Promise((resolve, reject) => {
bindFirestoreRef('accounts', fb.accounts.where('program', '==', payload))
.then((res) => {
commit('ACCOUNTS_READY', true);
resolve(res);
})
.catch((err) => {
commit('SNACKBAR_TEXT', err.message)
Sentry.captureException(err)
reject(err)
})
})
})
},
Related
I have this existing working VueJs code
const actions = {
retrieveStatus({ rootState, commit, dispatch }) {
return Axios
.get('/abc/GetStatus', {
params: {
draftId: rootState.eform.Id
}
})
.then(response => {
commit('SET_STATUS', response.data.statusCode);
return response.data;
})
.catch(err => {
throw new Error('Errors');
})
},
I don't see anywhere it uses dispatch but it exists there.
I'm creating app in Vue.js. I have two similar methods:
editTool() {
return ToolService.editTool(this.id, this.editedItem)
.then((response) => {
this.$refs.dialogInfo.setSuccess(response);
})
.catch((error) => {
this.$refs.dialogInfo.setError(error);
})
.finally(() => {
this.$emit("completed");
});
},
newTool() {
return ToolService.addTool(this.editedItem)
.then((response) => {
this.$refs.dialogInfo.setSuccess(response);
})
.catch((error) => {
this.$refs.dialogInfo.setError(error);
})
.finally(() => {
this.$emit("completed");
});
},
They only differ in the value in return, rest is the same. I want to turn these two methods into one. I tried this way:
newMethod() {
if (this.edit) return ToolService.editTool(this.id, this.editedItem);
else
return ToolService.addTool(this.editedItem)
.then((response) => {
this.$refs.dialogInfo.setSuccess(response);
})
.catch((error) => {
this.$refs.dialogInfo.setError(error);
})
.finally(() => {
this.$emit("completed");
});
},
But it doesn't work correct. How to do it?
Try in the following way.
editTool() {
return ToolService.editTool(this.id, this.editedItem)
},
newTool() {
return ToolService.addTool(this.editedItem)
},
newMethod() {
let method = this.newTool;
if (this.edit) method = this.editTool;
return method().then((response) => {
this.$refs.dialogInfo.setSuccess(response);
})
.catch((error) => {
this.$refs.dialogInfo.setError(error);
})
.finally(() => {
this.$emit("completed");
});
}
I have Login.vue component which has method postData()
postData() {
this.$store.dispatch('doLogin', fdata)
},
doLogin is in store.js
actions: {
doLogin({ commit }, loginData) {
commit('loginStart');
axios.post(this.state.apiURL+'/login', {
...loginData
})
.then(response => {
commit('loginStop', null);
commit('updateAccessToken', response.data.access_token);
})
.catch(error => {
commit('loginStop', error);
})
},
how to invoke back from router.js a method in other component, let say again from Login.vue?
I want in general invoke Toast which is bootstrap thing in Login.vue methods.
Any idea?
Login.vue component method
this.$store.dispatch('doLogin', fdata).then(response => {
console.log("response from promise:",response)
}, error => {
this.makeToast(true, error, 'danger', 'b-toaster-top-center')
console.error("Got error:",error)
})
store.js Actions
doLogin({ commit }, loginData) {
return new Promise((resolve, reject) => {
console.log("store - doLogin", loginData)
commit('loginStart');
axios.post(this.state.apiURL+'/login', {
...loginData
})
.then(response => {
localStorage.setItem('accessToken', response.data.access_token);
commit('loginStop', null);
commit('updateAccessToken', response.data.access_token);
router.push('/profile');
resolve(response);
})
.catch(error => {
console.log('error', error)
commit('loginStop', error);
commit('updateAccessToken', null);
reject(error)
})
})
},
I need to call 2 actions from inside my component, but the second should only start after the first one has 100% finished it's job.
I'm trying this but it doesn't work
mounted() {
this.$store.dispatch('coinModule/loadApiCoins')
.then(() => {
this.$store.dispatch('coinModule/loadUserCoins')
})
.catch(error => {
console.log(error)
});
},
and the 2 actions are these
loadApiCoins({ commit, dispatch, rootGetters }) {
Vue.axios({
method: 'get',
url: 'https://api.coinmarketcap.com/v1/ticker/',
transformRequest: [(data, headers) => {
delete headers.common.Authorization
return data
}]
})
.then(response => { commit('SET_API_COINS', response.data) })
.catch(error => { console.log(error) })
},
loadUserCoins({ commit, dispatch, rootGetters }) {
Vue.axios.get('http://127.0.0.1:8000/api/coins/')
.then(response => {
commit('SET_USER_COINS', response.data)
commit('SET_USER_PORTFOLIO_OVERVIEW')
})
.catch(error => { console.log(error) })
}
These should be the other way around.
Screen of my network tab
When you dispatch an action, it doesn't have a then callback by default. That's only the case if the action returns a Promise. Your axios.get call should return a Promise, but you aren't returning it in your action. You should simply return it and then then callback will fire in your mounted hook.
loadApiCoins({ commit, dispatch, rootGetters }) {
return Vue.axios({
method: 'get',
url: 'https://api.coinmarketcap.com/v1/ticker/',
transformRequest: [(data, headers) => {
delete headers.common.Authorization
return data
}]
})
.then(response => { commit('SET_API_COINS', response.data) })
.catch(error => { console.log(error) })
},
I use VueX in my VueJs app and I need to close pre-loader after I got an answer from server for 4 my get requests. I try to use callback function to change pre-loader state but it changes after requests STARTs, but I need to change pre-loader state after all requests SUCCESS. Below is my code:
Index.vue
<template>
<div class="index">
<div class="content-is-loading"
v-if="appIsLoading"></div>
<div v-else class="index__wrapper">
<navbarInner></navbarInner>
<div class="index__content">
<sidebar></sidebar>
<router-view></router-view>
</div>
<foo></foo>
</div>
</div>
</template>
<script>
import NavbarInner from './NavbarInner'
import Sidebar from './Sidebar'
import Foo from './../Foo'
import Shows from './Shows/Shows'
import Dashboard from './Dashboard'
import { API_URL } from '../../../config/constants'
import { mapState } from 'vuex'
export default {
name: 'index',
data () {
return {
appIsLoading: true,
bandName: ''
}
},
components: {
NavbarInner,
Sidebar,
Foo,
Shows,
Dashboard
},
created () {
function loadData (context, callback) {
// Loading bands for the user
context.$store.dispatch('getBands')
// Loading contacts for the user
context.$store.dispatch('getContacts')
// Loading merch for the user
context.$store.dispatch('getInventory')
// Loading tours for the active band
context.$store.dispatch('getToursList')
callback(context)
}
loadData(this, function (context) {
context.appIsLoading = false
})
}
}
Below I add code of one of the request:
api/tour.js
import axios from 'axios'
import { API_URL } from '../../config/constants'
export default {
getToursList () {
return new Promise((resolve, reject) => {
let bandId = window.localStorage.getItem('active_band_id')
let token = window.localStorage.getItem('token')
axios.get(API_URL + '/api/bands/' + bandId + '/tours/', {
headers: {'x-access-token': token}
})
.then((result) => {
return resolve(result.data)
})
.catch(err => reject(err))
})
},
getInventory () {
return new Promise((resolve, reject) => {
let token = window.localStorage.getItem('token')
axios.get(API_URL + '/api/merch/listProductForUser/1000/0', {
headers: {'x-access-token': token}
})
.then((response) => {
let items = response.data
return resolve(items)
})
.catch((err) => {
return reject(err)
})
})
},
getContacts () {
return new Promise((resolve, reject) => {
let token = window.localStorage.getItem('token')
axios.get(API_URL + '/api/contact/get_contacts_for_user/1000/0', {
headers: {'x-access-token': token}
})
.then((response) => {
console.log(response.data)
let contacts = response.data
return resolve(contacts)
})
.catch((err) => {
return reject(err)
})
})
},
getBands () {
return new Promise((resolve, reject) => {
let token = window.localStorage.getItem('token')
axios.get(API_URL + '/api/band/getBandsForUser/1000/0', {
headers: {'x-access-token': token}
})
.then((response) => {
console.log(response.data)
let bands = response.data
return resolve(bands)
})
.catch((err) => {
return reject(err)
})
})
}
}
Vuex/tour.js
import api from '../../api/onload'
import * as types from '../mutation-types'
const state = {
tours: [],
contacts: [],
bands: [],
merch: [],
success: false,
loading: false
}
const actions = {
getToursList ({commit}) {
api.getToursList()
.then((tours) => {
commit(types.RECEIVE_TOURS, tours)
}).catch((err) => {
console.error('Error receiving tours: ', err)
commit(types.RECEIVE_TOURS_ERROR)
})
},
getInventory ({commit}) {
api.getInventory()
.then((items) => {
commit(types.RECEIVE_INVENTORY, items)
})
.catch((err) => {
console.error('Error receiving inventory: ', err)
commit(types.RECEIVE_INVENTORY_ERROR)
})
},
getBands ({commit}) {
api.getBands()
.then((bands) => {
commit(types.RECEIVE_BANDS, bands)
})
.catch((err) => {
console.error('Error receiving bands: ', err)
commit(types.RECEIVE_BANDS_ERROR)
})
},
getContacts ({commit}) {
api.getContacts()
.then((contacts) => {
commit(types.RECEIVE_CONTACTS, contacts)
})
.catch((err) => {
console.error('Error receiving bands: ', err)
commit(types.RECEIVE_CONTACTS_ERROR)
})
}
}
const mutations = {
[types.RECEIVE_TOURS] (state, tours) {
state.tours = tours
},
[types.RECEIVE_INVENTORY] (state, items) {
state.items = items
},
[types.RECEIVE_BANDS] (state, bands) {
state.bands = bands
},
[types.RECEIVE_CONTACTS] (state, contacts) {
state.contacts = contacts
console.log(state.contacts)
}
}
export default {
state, mutations, actions
}
How should I change the code?
The code you posted doesn't actually wait on the response from any of the actions you are calling.
You could also move everything to a method and refactor.
Finally I've assumed your actions return a Promise i.e.
created () {
this.getAll()
},
methods: {
getAll () {
Promise.all([
this.$store.dispatch('getBands'),
this.$store.dispatch('getContacts'),
this.$store.dispatch('getInventory'),
this.$store.dispatch('getToursList'),
])
.then(responseArray => {
this.appIsLoading = false
})
.catch(error => { console.error(error) })
EDIT
To get your actions to resolve as you need them (when the mutations have fired and your store is updated) you need to wrap them in a Promise:
Vuex/tour.js (actions object)
getToursList: ({ commit }) =>
new Promise((resolve, reject) => {
api.getToursList()
.then((tours) => {
commit(types.RECEIVE_TOURS, tours)
resolve()
}).catch((err) => {
console.error('Error receiving tours: ', err)
commit(types.RECEIVE_TOURS_ERROR)
reject()
})
})