Call an action from within another action - vue.js

I have the following setup for my actions:
get1: ({commit}) => {
//things
this.get2(); //this is my question!
},
get2: ({commit}) => {
//things
},
I want to be able to call one action from within another, so in this example I want to be able to call get2() from within get1(). Is this possible, and if so, how can I do it?

You have access to the dispatch method in the object passed in the first parameter:
get1: ({ commit, dispatch }) => {
dispatch('get2');
},
This is covered in the documentation.

You can access the dispatch method through the first argument (context):
export const actions = {
get({ commit, dispatch }) {
dispatch('action2')
}
}
However, if you use namespaced you need to specify an option:
export const actions = {
get({ commit, dispatch }) {
dispatch('action2', {}, { root: true })
}
}

for actions that does not require payload
actions: {
BEFORE: async (context, payload) => {
},
AFTER: async (context, payload) => {
await context.dispatch('BEFORE');
}
}
for actions that does require payload
actions: {
BEFORE: async (context, payload) => {
},
AFTER: async (context, payload) => {
var payload = {}//prepare payload
await context.dispatch('BEFORE', payload);
}
}

we can pass parameters also while dispatching.
dispatch('fetchContacts', user.uid);

export actions = {
GET_DATA (context) {
// do stuff
context.dispatch('GET_MORE_DATA');
},
GET_MORE_DATA (context) {
// do more stuff
}
}

Related

beforeRouteUpdate not recognizing function

When I try to call this.getData() in beforeRouteUpdate it just spits out this error
"TypeError: this.getData is not a function"
From looking at other peoples examples this work, but they weren't using async/await.
<script>
export default {
async beforeRouteUpdate(to, from, next) {
await this.getData()
next()
},
data() {
return {
word: null,
}
},
async created() {
await this.getData()
},
methods: {
async getData() {
const resp = await axios.get(
'http://127.0.0.1:8000/api/word/' + this.$route.params.word,
{ validateStatus: false }
)
console.log(resp)
switch (resp.status) {
case 200:
this.word = {
word: resp.data.word,
definition: resp.data.definition,
}
break
case 404:
this.word = null
break
}
},
},
}
</script>
The concept you want use it, is called "prefetch".
It's better use this solution:
beforeRouteEnter(to, from, next) {
axios.get(
'http://127.0.0.1:8000/api/word/' + this.$route.params.word,
{validateStatus: false}
)
.then(resp => {
next()
})
.catch(error => {
})
},
beforeRouteUpdate(to, from, next) {
axios.get(
'http://127.0.0.1:8000/api/word/' + this.$route.params.word,
{validateStatus: false}
)
.then(resp => {
next()
})
.catch(error => {
})
}
NOTE 1: You don't access to this in beforeRouteEnter (to use methods). Because your component doesn't mounted yet.
NOTE 2: To avoid fetch duplication (DRY principle), you can modularize fetching (like vuex actions) and call it.

How can I update the comments without refreshing it?

First, I'm using vuex and axios.
store: commentService.js
components:
CommentBox.vue (Top components)
CommentEnter.vue (Sub components)
This is the logic of the code I wrote.
In the store called commentService.js, there are mutations called commentUpdate.
And There are actions called postComment and getComment.
At this time, In the component called CommentBox dispatches getComment with async created().
Then, in getComment, commentUpdate is commited and executed.
CommentUpdate creates an array of comments inquired by getComment and stores them in a state called commentList.
Then I'll get a commentList with "computed".
CommentEnter, a sub-component, uses the commentList registered as compounded in the CommentBox as a prop.
The code below is commentService.js.
import axios from 'axios'
export default {
namespaced: true,
state: () => ({
comment:'',
commentList: []
}),
mutations: {
commentUpdate(state, payload) {
Object.keys(payload).forEach(key => {
state[key] = payload[key]
})
}
},
actions: {
postComment(state, payload) {
const {id} = payload
axios.post(`http://??.???.???.???:????/api/books/${id}/comments`, {
comment: this.state.comment,
starRate: this.state.starRate
}, {
headers: {
Authorization: `Bearer ` + localStorage.getItem('user-token')
}
})
.then((res) => {
console.log(res)
this.state.comment = ''
this.state.starRate = ''
)
.catch((err) => {
alert('댓글은 한 책당 한 번만 작성할 수 있습니다.')
console.log(err)
this.state.comment = ''
this.state.starRate = ''
})
},
async getComment({commit}, payload) {
const {id} = payload
axios.get(`http://??.???.???.???:????/api/books/${id}/comments`)
.then((res) => {
console.log(res)
const { comment } = res.data.commentMap
commit('commentUpdate', {
commentList: comment
})
})
.catch((err) => {
console.log(err)
commit('commentUpdate', {
commentList: {}
})
})
}
}
}
The code below is CommentBox.vue
computed: {
commentList() {
return this.$store.state.commentService.commentList
}
},
methods: {
async newComment() {
if(this.$store.state.loginService.UserInfoObj.id === '') {
alert('로그인 후 이용할 수 있습니다.')
return
}
this.$store.dispatch('commentService/postComment', {
id: this.$route.params.id,
comment: this.$store.state.comment,
starRate: this.$store.state.starRate
})
}
},
async created() {
this.$store.dispatch('commentService/getComment', {
id: this.$route.params.id
})
}
The code below is CommentEnter.vue
created() {
this.userComment = this.comment
},
props: {
comment: {
type: Object,
default: () => {}
}
},
I asked for a lot of advice.
There were many comments asking for an axios get request after the axios post request was successful.
In fact, I requested an axios get within .then() of the axios post, and the network tab confirmed that the get request occurred normally after the post request.
But it's still not seen immediately when I register a new comment.
I can only see new comments when I refresh it.
How can I make a new comment appear on the screen right away when I register it?
Can't you just call getComment when postComment is finished?
methods: {
async newComment() {
if(this.$store.state.loginService.UserInfoObj.id === '') {
alert('로그인 후 이용할 수 있습니다.')
return
}
this.$store.dispatch('commentService/postComment', {
id: this.$route.params.id,
comment: this.$store.state.comment,
starRate: this.$store.state.starRate
}).then(function() {
this.$store.dispatch('commentService/getComment', {
id: this.$route.params.id
})
})
}
},
}
Or since you're using async:
methods: {
async newComment() {
if(this.$store.state.loginService.UserInfoObj.id === '') {
alert('로그인 후 이용할 수 있습니다.')
return
}
await this.$store.dispatch('commentService/postComment', {
id: this.$route.params.id,
comment: this.$store.state.comment,
starRate: this.$store.state.starRate
})
this.$store.dispatch('commentService/getComment', {
id: this.$route.params.id
})
}
},
}

Call Vuex action from component after mutation is complete

I have a Vue component that calls a Vuex action in its create hook (an api.get that fetches some data, and then dispatches a mutation). After that mutation is completed, I need to call an action in a different store, depending on what has been set in my store's state... in this case, getUserSpecials.
I tried to use .then() on my action, but that mutation had not yet completed, even though the api.get Promise had resolved, so the store state I needed to check was not yet available.
Does anyone know if there is a 'best practice' for doing this? I also considered using a watcher on the store state.
In my component, I have:
created () {
this.getUserModules();
if (this.userModules.promos.find((p) => p.type === 'specials')) {
this.getUserSpecials();
}
},
methods: {
...mapActions('userProfile', ['getUserModules',],),
...mapActions('userPromos', ['getUserSpecials',],),
},
In my store I have:
const actions = {
getUserModules ({ commit, dispatch, }) {
api.get(/user/modules).then((response) => {
commit('setUserModules', response);
});
},
};
export const mutations = {
setUserModules (state, response) {
Object.assign(state, response);
},
};
Right now, the simple if check in my create hook works fine, but I'm wondering if there is a more elegant way to do this.
[1] Your action should return a promise
getUserModules ({ commit, dispatch, }) {
return api.get(/user/modules).then((response) => {
commit('setUserModules', response);
})
}
[2] Call another dispatch when the first one has been resolved
created () {
this.getUserModules().then(response => {
this.getUserSpecials()
})
}
Make your action return a promise:
Change:
getUserModules ({ commit, dispatch, }) {
api.get(/user/modules).then((response) => {
commit('setUserModules', response);
});
},
To:
getUserModules({commit, dispatch}) {
return new Promise((resolve, reject) => {
api.get(/user/modules).then((response) => {
commit('setUserModules', response);
resolve(response)
}).catch((error) {
reject(error)
});
});
},
And then your created() hook can be:
created () {
this.getUserModules().then((response) => {
if(response.data.promos.find((p) => p.type === 'specials'))
this.getUserSpecials();
}).catch((error){
//error
});
},

How to update Vue component's property with fetched data

Vue.component('test', {
template: `some html`,
data() {
{
return {
somedata: 'hey, starting!'
}
}
},
methods: {
fetchdata: function fetchdata() {
fetch('http://localhost:5000/getmesome')
.then(response => response.json()).then(data => this.somedata = data
);
}
}, created() {
this.fetchdata();
console.log(this.somedata); //returns 'hey starting' not the fetched data.
}
});
As shown in the code comment, this is not refreshing the property with the fetched data. How can I do it?
Thanks.
fetchdata() will return immediately while the request is still in progress since it is an async operation. console.log(this.somedata) will be executed before the fetch operation has completed.
This is a basic async misunderstanding; I would suggest you read up on asynchronous JavaScript topics (promises, async and await, etc).
Either of these solutions will work:
methods: {
fetchdata() {
return fetch('http://localhost:5000/getmesome')
.then(response => response.json())
.then(data => this.somedata = data);
}
},
created() {
this.fetchdata()
.then(() => console.log(this.somedata));
}
methods: {
async fetchdata() {
const res = await fetch('http://localhost:5000/getmesome');
const data = await res.json();
this.somedata = data;
}
},
async created() {
await this.fetchdata();
console.log(this.somedata);
}

Vuex update state by using store actions

I have two functions in my store, one that gets data by calling API and one that toggles change on cell "approved". Everything working fine, except that when I toggle this change it happens in database and I get the response that it is done but It doesn't update on UI.
I am confused, what should I do after toggling change to reflect change on UI, should I call my API from .then or should I call action method responsible for getting data from server.
export default {
state: {
drivers: {
allDrivers:[],
driversError:null
},
isLoading: false,
token: localStorage.getItem('token'),
driverApproved: null,
driverNotApproved: null
},
getters: {
driversAreLoading (state) {
return state.isLoading;
},
driverError (state) {
return state.drivers.driversError;
},
getAllDrivers(state){
return state.drivers.allDrivers
}
},
mutations: {
getAllDrivers (state) {
state.isLoading=true;
state.drivers.driversError=null;
},
allDriversAvailable(state,payload){
state.isLoading=false;
state.drivers.allDrivers=payload;
},
allDriversNotAvailable(state,payload){
state.isLoading=false;
state.drivers.driversError=payload;
},
toggleDriverApproval(state){
state.isLoading = true;
},
driverApprovalCompleted(state){
state.isLoading = false;
state.driverApproved = true;
},
driverApprovalError(state){
state.isLoading = false;
state.driverError = true;
}
},
actions: {
allDrivers (context) {
context.commit("getAllDrivers")
return new Promise((res,rej)=>{
http.get('/api/admin/getAllDrivers').then(
response=>{
if (response.data.success){
let data=response.data.data;
data=data.map(function (driver) {
return {
/* response */
};
});
context.commit("allDriversAvailable",data);
res();
}else {
context.commit("allDriversNotAvailable",response.data)
rej()
}
})
.catch(error=>{
context.commit("allDriversNotAvailable",error.data)
rej()
});
});
},
toggleDriverApproval (context, payload){
return new Promise((res, rej)=>{
http.post("/api/admin/toggleDriverApproval",{
driver_id: payload
})
.then( response => {
context.commit('driverApprovalCompleted');
res();
}).catch( error =>{
context.commit('driverApprovalError');
rej()
})
})
}
}
}
and here is the code on the view, I wrote the necessary code for better clarification of the problem
export default {
name: 'Drivers',
data: () => ({
data: [],
allDrivers: [],
driversErrors: []
}),
created() {
this.$store
.dispatch('allDrivers')
.then(() => {
this.data = this.$store.getters.getAllDrivers
})
.catch(() => {
this.errors = this.$store.getters.driverError
})
},
computed: {
isLoading() {
return this.$store.getters.driversAreLoading
}
},
methods: {
verify: function(row) {
console.log(row)
this.$store.dispatch('toggleDriverApproval', row.id).then(() => {
this.data = this.$store.getters.getAllDrivers
console.log('done dis')
})
},
},
}
if I understand your issue, you want the UI displaying your data to change to the updated data after making a post request.
If you are using Vuex you will want to commit a mutation, and use a getter display the data.
I am not sure how your post request is being handled on the server but if successful typically you would send a response back to your front end with the updated data, and commit a mutation with the updated data.
Example:
Make a Post request
toggleDriverApproval (context, payload){
return new Promise((res, rej)=>{
http.post("/api/admin/toggleDriverApproval",{
driver_id: payload
})
.then( response => {
context.commit('driverApprovalCompleted', response.data);
res();
}).catch( error =>{
context.commit('driverApprovalError', error.response.data);
rej()
})
})
}
If succesful commit the mutation
.then( response => {
context.commit('driverApprovalCompleted', response.data);
res();
})
response.data being your data you want to mutate the state with.
Mutation Example:
customMutation(state, data) {
state.driverApproval = data
}
Getter Example:
driver(state) {
return state.driverApproval
}
displaying the getter in a template
<template>
<div v-if="driver">{{driver}}</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
name: Example,
computed: {
driver() {
return this.$store.getters.driver
},
// or use mapGetters
...mapGetters(['driver'])
}
}
</script>
more examples can be found at Vuex Docs