Home > Mobile >  Emit does not work inside axios call, but does outside
Emit does not work inside axios call, but does outside

Time:01-04

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

  • Related