1

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.

7
  • 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. Commented Nov 27, 2017 at 21:27
  • 1
    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. Commented Nov 27, 2017 at 21:49
  • "vue": "^2.5.8" Commented Nov 27, 2017 at 21:50
  • 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. Commented Nov 27, 2017 at 21:52
  • I also tried something like that v-on:loadingMessage="updateLoadingMessage($event)" v-on:isLoading="updateLoadingStatus($event)" and it didn't work neither. Commented Nov 28, 2017 at 0:38

2 Answers 2

2

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

<loading
  v-show="isLoading"
  :loadingMessage="loadingMessage"
  :isGiphy="true">
</loading>
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, I've been struggling with a similar issue since yesterday. I can't understand how a v-if has something to do with this. Did you figure it out by your own or you received an official response from VueJS community? Thanks again!
0

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.

2 Comments

If VueJs v-if handles component destruction and re-creation, shouldn't the framework create the components again with its corresponding events attached? I can't figure out this behaviour with that directive.
Yes, that's the same idea I had. However, the component that is catching for the event is component-b. And when the isLoading is set to true, that component-b is not in the template and is not able to catch the event from the child component simple because it is not there. That's why it works with v-show.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.