1
votes

I'm facing a consistent behavior in vue.js and could not find any documentation describing it, and what I would like to know is if this behavior is expected or not.

I'm developing an SPA using vue.js 2.6.8, @vue/cli-service 3.5.1, and vue-router 3.0.1.

My routes look like this:

{
    path: '/Users',
    name: 'Users',
    component: Users,
    meta: {requiresAuth: true},
    children: [
        {
            path: 'New',
            component: NewUserModal,
            name: 'NewUserModal',
            meta: {requiresAuth: true},
        }
    ]
},
{
    path: '/Users/:id',
    component: User,
    name: 'User',
    meta: {requiresAuth: true}
}

The Users component is a listing of users and has a router-view inside it in order to render the new user modal, looking like this:

<template>
    <my-list-component>
        <template #modal>
            <router-view/>
        </template>
    </my-list-component>
</template>

Inside this component the user can click a button to push a new route, NewUserModal, then a modal is shown where one can create a new user and after the operation is completed another router push happens, so the user is redirected to the User route. In short:

Users -> NewUserModal -> User

All stages of the NewUserModal happen, it is indeed destroyed. However, if I ever go back to the Users component the DOM element of NewUserModal is still there, so I see the DOM of a destroyed child component on the parent.

I have the impression that the route change occurred "too fast" and when the destroy method of NewUserModal component is invoked the DOM element that it needs to destroy is not present in the page anymore, so it cannot remove it.

As a workaround I placed a the following code inside NewUserModal:

    beforeDestroy() {
        this.$el.remove();
    },

And everything works just fine. So, is this the expected behavior of vue? Am I missing something? And more importantly, can I wait the DOM removal before changing routes?

Thank you for your time and help!

Edit 1

For a better understanding, my app.vue has a keep alive, so all the direct children (e.g. the lists of users, etc) do not send a request to the server every single time they are activated. The structure of the App.vue looks like this:

<template>
    <keep-alive>
        <router-view/>
    </keep-alive>
</template>

Edit 2

In some further tests I discovered the workaround was not working in 100% of cases, so I changed the workaround to this:

async redirectToUser(userId) {
    this.$router.push({name: 'Users'});

    await this.$nextTick();

    this.$router.push({
        name: 'User',
        params: {id: userId}
    });
},

This way I first redirect the user to the parent component, so the life cycle of the child component is fully executed and its DOM elements removed, and then the user is redirected to the User component.

Working 100% of the time so far.

1
"However, if I ever go back to the Users component" πŸ‘ˆ how are you "going back"? – Phil
You could try changing to <router-view :key="$route.fullPath"></router-view>. See What is <router-view :key=β€œ$route.fullPath”> ? – Phil
Apparently, Component User is children of component Users. Can you add it inside it and check? – Varit J Patel
@Phil all the route changes occur via push, for example: this.$router.push({name: 'User'}); – marceloatg
@varit05 I might be wrong, but it is not the case. The component Users is a list of users, and the component User is the details of a single user. User is not a child route because it does not depend on the Users component, it is in fact a completely different page. If I added the User as a child route of Users I would have to deal with the list of users, that should not be shown. – marceloatg

1 Answers

0
votes

So as you route to user/:id the component instance will be reused. For any id- as per docs.

Maybe Navigation Guards can help?

e.g:

beforeRouteUpdate (to, from, next) {
    // called when the route that renders this component has changed,
    // but this component is reused in the new route.
    // For example, for a route with dynamic params `/foo/:id`, when we
    // navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
    // will be reused, and this hook will be called when that happens.
    // has access to `this` component instance.
  },

And more importantly, can I wait the DOM removal before changing routes?

async beforeRouteLeave (to, from, next) {
    // called when the route that renders this component is about to
    // be navigated away from.
    // has access to `this` component instance.

    // maybe wait for next render
    await this.nextTick();
    next()
  }