I want to emit data to a parent component after a succesful axios call, but for some reason emit does not work when resolving the promise. Emit works fine when putting it before or after the axios call.
I can't seem to figure out what's wrong. I'm using arrow functions so this
shouldn't be changed, and even then - I tried doing it with functions and assigning this
to a variable before going into the axios call but that didn't work either.
I made a custom config for axios, but changing it back to the default does not change anything either.
// ApiWrapper.js
// this is the custom axios config I'm using
import axios from 'axios';
const apiWrapper = axios.create({
baseURL: 'my_url',
})
apiWrapper.interceptors.request.use(function(config) {
let hasToken = localStorage['token'] || false;
if(hasToken) {
config.headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`
}
return config
}, function(error) {
return Promise.reject(error);
})
export default apiWrapper;
My App, which is the parent and listens to the token
event.
<script>
// App.vue
import { generalState } from './helpers/GeneralState';
import Loading from './components/generic/Loading.vue';
import LoginPage from './views/LoginPage.vue';
export default {
name: 'App',
components: {
Loading,
LoginPage
},
data() {
return {
token: null,
generalState: generalState
}
},
methods: {
setToken(token) {
console.log('in setToken');
console.log(token);
this.token = token;
localStorage.setItem('token', token);
}
}
}
</script>
<template>
<div >
<div >
<section >
<div >
<div >
<div >
<template v-if="generalState.isLoading">
<Loading/>
</template>
<template v-else-if="!token">
<LoginPage @token="setToken" />
</template>
<template v-else>
<router-view/>
</template>
</div>
</div>
</div>
</section>
</div>
</div>
</template>
And finally, my LoginPage which should emit the returned token
to the parent component.
// LoginPage.vue
<script>
import Input from '../components/generic/Input.vue';
import Button from '../components/generic/Button.vue';
import apiWrapper from '../helpers/ApiWrapper';
import { generalState } from '../helpers/GeneralState';
export default {
components: {
Input,
Button
},
data() {
return {
form: {
username: '',
password: ''
},
}
},
methods: {
process_form(e) {
generalState.isLoading = true;
let token = null;
this.$emit('token', 'TOKEN-BEFORE');
apiWrapper.post('login/', {
email: this.form.username,
password: this.form.password
}).then(resp => {
token = resp.data.token;
this.$emit('token', token);
}).finally(() => {
generalState.isLoading = false;
});
this.$emit('token', 'TOKEN-AFTER');
}
},
}
</script>
<template>
<div >
<div >
<div >
<h4 >Sign in</h4>
<form v-on:submit.prevent="process_form">
<p >Manage all your integrations.</p>
<div >
<Input v-model="form.username" inputType="text" id="username" name="username" placeholder="Username"/>
</div>
<div >
<Input v-model="form.password" inputType="password" id="password" name="password" placeholder="Password" />
</div>
<div >
<Button buttonType="secondary" role="submit">
Log in
</Button>
<a href="#!">Forgot password?</a>
</div>
<div >
<p >Don't have an account?</p>
<Button buttonType="primary" role="button">
Sign up
</Button>
</div>
</form>
</div>
</div>
<div >
<div >
<h4 >One platform for all integrations.</h4>
<p >
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
</p>
</div>
</div>
</div>
</template>
This is the part that is not working:
methods: {
process_form(e) {
generalState.isLoading = true;
let token = null;
this.$emit('token', 'TOKEN-BEFORE'); // <-- This emit DOES work
apiWrapper.post('login/', {
email: this.form.username,
password: this.form.password
}).then(resp => {
token = resp.data.token;
this.$emit('token', token); // <-- This emit DOES NOT work
}).finally(() => {
generalState.isLoading = false;
});
this.$emit('token', 'TOKEN-AFTER'); // <-- This emit DOES work
}
},
I'm pulling my hair out because I cannot see why the emit inside the axios is not working. Can anybody help me out? Thanks in advance.
CodePudding user response:
Your LoginPage component is displayed using a v-else-if :
<!-- when generalState.isLoading is true -->
<template v-if="generalState.isLoading">
<Loading/>
</template>
<!-- that component get removed,
which means the token event listener get also removed -->
<template v-else-if="!token">
<LoginPage @token="setToken"/>
</template>
What you can do is :
1 / don't use v-else-if, but a simple v-if instead, so your component is not removed
2 / Use a v-show to hide your component without removing it, because you might encounter styling issues, it's cleaner to hide it so your loader is displaying fine.
<!-- when generalState.isLoading is true -->
<template v-if="generalState.isLoading">
<Loading/>
</template>
<!-- that component do not get removed but just hidden -->
<template v-if="!token" v-show="!generalState.isLoading">
<LoginPage @token="setToken"/>
</template>
What is happening is that your axios callback get actually called, but your component has been removed by then and is going to be destroyed, so the parent component no longer listen to the events that the component is emitting