1
votes

I have a child component which fetch some data from my server, before fetching I change the loading status to true and I want to set it to false after the fetch is completed. So I do something like that in my child component:

mounted() {
    this.$emit('update:loadingMessage', 'Loading version options from Artifactory...');
    this.$emit('update:isLoading', true);
    this.fetchVersions();
},

methods: {
    fetchVersions() {
        const promises = [
            this.$http.get(`${process.env.API_URL}/version/front`),
            this.$http.get(`${process.env.API_URL}/version/back`),
        ];
        Promise.all(promises)
            .then((values) => {
                // Do some stuff
            })
            .then(() => {
                this.$emit('update:isLoading', false);
            })
            .catch(requestService.handleError.bind(this));
    },
},

And in my parent component I listen to this event like that:

<version-selector
    :form="form"
    @update:loadingMessage="updateLoadingMessage"
    @update:isLoading="updateLoadingStatus"
    :isSnapshotVersion="isSnapshotVersion">
</version-selector>

Finally in the updateLoadingStatus I set the isLoading value to true or false accordingly.

updateLoadingMessage(message) {
  this.$log.debug(message);
  this.loadingMessage = message;
},
updateLoadingStatus(status) {
  this.$log.debug(status);
  this.isLoading = status;
},

This is useful to display or not my loading component:

<loading
    v-if="isLoading"
    :loadingMessage="loadingMessage"
    :isGiphy="true">
</loading>

My problem is that the first emit is working and the isLoading value is set to true but the second one is not working and my isLoading value stay to true forever... In my method updateLoadingStatus I log the status value and I see that this method is just called once.

2
Important considerations: First, you're using this.$emit() in a .then() context. You could be running into issues where this is referencing a different context than the Vue instance. Consider doing something like var this_vue_instance = this; before entering the Promise, then using this_vue_instance.$emit() instead. As for your emit capturing issues, I haven't tested this myself, but perhaps the use of the colon in your events is producing issues? I would try eliminating it as a quick test to be sure that it's not conflicting syntax.B. Fleming
He's using arrow syntax which automatically binds this to the outer scope. The colon in the events wouldn't be an issue because Vue itself actually uses that for it's own events in Vue's .sync in one of the recent version. Which version of Vue are you using? Edit: The attribute key having a colon could be an issue.jostrander
"vue": "^2.5.8"Hugo
What you're describing is almost the same functionally that .sync provides in version 2.3.0 of Vue and later. vuejs.org/v2/guide/components.html#sync-Modifier in fact Vue itself uses the @update:key event, which may be the source of your problem.jostrander
I also tried something like that v-on:loadingMessage="updateLoadingMessage($event)" v-on:isLoading="updateLoadingStatus($event)" and it didn't work neither.Hugo

2 Answers

2
votes

I solved the problem by using v-show instead of v-if in my template.

<loading
  v-show="isLoading"
  :loadingMessage="loadingMessage"
  :isGiphy="true">
</loading>
0
votes

I know this is an old question, but I stumbled into a similar situation today, and finally understood why the mechanism was working with v-show, and not v-if.

If you get the following <template> tag:

<div>
    <component-a v-if="isLoading" />
    <component-b v-else @set-loading-status="setIsLoading" />
</div>

And the following <script> tag:

import ComponentA from './ComponentA'
import ComponentB from './ComponentB'
export default {
    name: 'ParentComponent',
    components: { ComponentA, ComponentB },
    data() {
        return {
            isLoading: false,
        }
    },
    methods: {
        setIsLoading(isLoading) {
            this.isLoading = isLoading
        },
    },
}

It seems fine, right? You can catch the set-loading-status event from the <component-b> and set it to true. But you can't catch it again to set it back to false.

But, let's take a look in the official Vue docs about v-if and v-show:

v-if is “real” conditional rendering because it ensures that event listeners and child components inside the conditional block are properly destroyed and re-created during toggles.

Now you can see that the component-b gets destroyed when isLoading is set to true, and you won't be able to catch the emitted event to change it back to false.

So, in this particular case, you must use v-show to handle the loading status.