Reputation: 566
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 class="flex h-screen bg-sgray">
<div class="m-auto">
<section class="h-full gradient-form md:h-screen">
<div class="container py-12 px-6 h-full">
<div class="flex justify-center items-center flex-wrap h-full g-6 text-gray-800">
<div class="block bg-white shadow-lg rounded-lg">
<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 class="lg:flex lg:flex-wrap g-0">
<div class="lg:w-6/12 px-4 md:px-0">
<div class="md:p-12 md:mx-6">
<h4 class="text-xl font-semibold mt-1 mb-12 pb-1">Sign in</h4>
<form v-on:submit.prevent="process_form">
<p class="mb-4">Manage all your integrations.</p>
<div class="mb-4">
<Input v-model="form.username" inputType="text" id="username" name="username" placeholder="Username"/>
</div>
<div class="mb-4">
<Input v-model="form.password" inputType="password" id="password" name="password" placeholder="Password" />
</div>
<div class="text-center pt-1 mb-12 pb-1">
<Button buttonType="secondary" role="submit">
Log in
</Button>
<a class="text-gray-500 hover:underline" href="#!">Forgot password?</a>
</div>
<div class="text-center pb-6">
<p class="mb-3 mr-2">Don't have an account?</p>
<Button buttonType="primary" role="button">
Sign up
</Button>
</div>
</form>
</div>
</div>
<div class="lg:w-6/12 flex items-center lg:rounded-r-lg rounded-b-lg lg:rounded-bl-none bg-sred">
<div class="text-white px-4 py-6 md:p-12 md:mx-6">
<h4 class="text-xl font-semibold mb-6">One platform for all integrations.</h4>
<p class="text-sm">
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.
Upvotes: 0
Views: 107
Reputation: 2462
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
Upvotes: 1