Vue $emit not getting executed in finally()

Viewed 409

In LoginForm.vue I have this function:

login() {
    event.preventDefault();
    let data = {
        email : this.email,
        password : this.password
    };
    if(this.remember)
        data['remember'] = 'on';
    this.$emit('toggle-load-overlay', true);
    fetch(this.loginRoute, {
        method : 'POST',
        body : JSON.stringify(data),
        headers : {
            "Content-Type": "application/json; charset=utf-8",
            'X-CSRF-TOKEN' : this.csrf,
        },
    })
    .then(data => data.json())
    .then(data => {
    if(!data['success'])
        this.errors = data['errors'];
    else {
        this.csrf = data['csrf'];
        this.$emit('loggedin', data['user']);
        this.$emit('update-token', data['csrf']);
    }
    })
    .catch(error => console.log(error))
    .finally(() => {
         this.$emit('toggle-load-overlay', false);
    });
}

Before fetching, I put a load overlay on the screen, by emitting an event to my AuthComponent.vue - this.$emit('toggle-load-overlay', true);

In AuthComponent.vue the event handler looks like this:

toggleLoadOverlay(displayOverlay) {
    this.isLoading = displayOverlay;
}

The first emit gets executed and overlay is put on. Then, in finally() I try to emit the same event again in order to hide the overlay. But it does not get executed. I debugged it, it gets in the finally(), but emit does not get executed.

Also, I've observed what is happening inside $emit method:

Vue.prototype.$emit = function (event) {
    var vm = this;
    {
        var lowerCaseEvent = event.toLowerCase();
        if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
            "Event \"" + lowerCaseEvent + "\" is emitted in component " +
            (formatComponentName(vm)) + " but the handler is registered for \"" + event + "\". " +
            "Note that HTML attributes are case-insensitive and you cannot use " +
            "v-on to listen to camelCase events when using in-DOM templates. " +
            "You should probably use \"" + (hyphenate(event)) + "\" instead of \"" + event + "\"."
            );
        }
    }
    var cbs = vm._events[event];
    if (cbs) {
        cbs = cbs.length > 1 ? toArray(cbs) : cbs;
        var args = toArray(arguments, 1);
        var info = "event handler for \"" + event + "\"";
        for (var i = 0, l = cbs.length; i < l; i++) {
        invokeWithErrorHandling(cbs[i], vm, args, vm, info);
        }
    }
    return vm
};

I've noticed one difference between these two emits. In this line var cbs = vm._events[event] the vm._events is not empty on first emit (it has the event toggleLoadOverlay), while on the second emit, it is empty.

Another observation, is that second emit works inside the final then() statement. So for now I will put it there, as well as in the catch(). But the intention of finally() was that the code wouldn't have to be duplicated.

  1. Why this.$emit('toggle-load-overlay', false) does not work in the finally() in my current code?
  2. Why does it work in then/catch?
  3. Is there a way to make it so that it would work in finally() as well?
1 Answers

It's working, can you check your parent component if event is being handled

please not: this is a general solution to test if $emit works in finally.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.js"></script>
</head>
<body>
    <div id="app">
        <div v-if="loading">Loading...</div>
        <test @set-loader="setLoader"></test>
    </div>

    <script>
        Vue.component('test',{
            template:'<div>Hi</div>',
            mounted:function(){
                fetch('https://jsonplaceholder.typicode.com/todos/1')
                .then(response => response.json())
                .then(json => console.log(json))
                .finally(() => {
                    console.log('child finally');
                    this.$emit('set-loader', false);
                });

            }
        });
        new Vue({
            el:'#app',
            data:{
                loading:true
            },
            methods:{
                setLoader: function(value){
                    console.log('parent event');
                    console.log(value);
                    this.loading = value;
                }
            }
        });
    </script>
</body>
</html>

Related