Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolving async data in router with async props #1923

Closed
FrEaKmAn opened this issue Dec 5, 2017 · 11 comments
Closed

Resolving async data in router with async props #1923

FrEaKmAn opened this issue Dec 5, 2017 · 11 comments

Comments

@FrEaKmAn
Copy link

FrEaKmAn commented Dec 5, 2017

What problem does this feature solve?

Idea it to handle all async data in the router stage, so the component already get's the data without a need for v-if checking. vue-router already has beforeRouteEnter and beforeRouteEnter, but I think it's too complicated because I need to reimplement both methods to be able to reuse component, the code overhead is to large and it also doesn't enable me to reuse component (same component, different data source).

What does the proposed API look like?

API proposes to add additional logic into router beforeEach (this is always called). Changes to router are

router.beforeEach((to, from, next) => {
    to.matched.forEach((match) => {
        Object.keys(match.props).forEach(propKey => {
            let props = match.props[propKey];
            if (typeof props === "boolean") {
                next();
            } else {
                if (!match._cache_props) {
                    match._cache_props = props;
                }

                match._cache_props(to).then((data) => {
                    match.props[propKey] = () => data;
                    next();
                });
            }
        });

    });
});

and then we simply make our props async

route = {
    path: '/products/:id',
    name: 'products.product',
    component: Product,
    props: async (route) => ({
        product: await http.get(`products/${route.params.id}`).then(r => r.data)
    })
}

This will first resolve the data, then render the component. All data will be passed to component through standard props.

Of course, there is additional works to handle all types of props and to have dependency resolves (I resolve something based on previous resolve).

@posva posva changed the title Resolving async data in router Resolving async data in router with async props Dec 5, 2017
@posva
Copy link
Member

posva commented Dec 5, 2017

At first, I was quite skeptical because it's adding another way of doing something
But as you said, it does make things more declarative and easy to test.
Regards implementation, I'm wondering if we shouldn't abstract some of the behaviour in components and leverage existing async components in Vue

@Mouvedia
Copy link

Mouvedia commented Jan 24, 2018

I stumbled on this problem today. This is badly needed: I assumed it was already implemented.

@NasskalteJuni
Copy link

Any news regarding this? I would also like to resolve data before passing it to the view (angular had this with resolve, it would be quite handy to have something like it without depending on the store)

@wlkns
Copy link

wlkns commented Jun 17, 2018

I'm also struggling with this, @FrEaKmAn's first pass is good but as he detailed it is also incomplete.

This is the only thing preventing us from a full move away from Angular1.

@posva
Copy link
Member

posva commented Jun 22, 2018

To be clear, async data is already feasible in the current state of vue-router and it is documented. This feature would make things more declarative and easier to test

@nwrox
Copy link

nwrox commented Jun 23, 2018

@posva where this is documented, can you provide the link?

@posva
Copy link
Member

posva commented Jun 23, 2018

https://router.vuejs.org/guide/advanced/data-fetching.html

@AminZoubaa
Copy link

AminZoubaa commented Jan 21, 2019

Hey @FrEaKmAn,
thanks for your idea and inspiration,
i extended your idea a little bit, have a look at: https://gist.github.com/AminZoubaa/23724284350f2ea2a902cc77afa93d2f

Check out the repo for a small demo: https://github.com/AminZoubaa/vue-async-router-demo

@wlkns maybe that could help you too

@bpolaszek
Copy link

Here are my 2 cents:

# Todolist.vue
<template>
    <ul>
        <li v-for="todo in todos" :key="todo.id">
            {{ todo.text }}
        </li>
    </ul>
</template>
<script>
    export default {
        props: {
            todos: {
                type: Array,
                required: true,
            },
        },
    }
</script>
# router.js

import VueRouter from "vue-router";

const hydrateProps = (route, props) => {
    Object.assign(route.meta, {props});
}

return new VueRouter({
    routes: [
        {
            path: '/todos',
            name: 'todolist',
            async beforeEnter(to, from, next) {
                const todos = await someService.retrieve('/todos');
                hydrateProps(to, {todos});
                next();
            },
            props: (route) => ({
                todos: route.meta.props.todos
            }),
            component: () => import('Todolist'),
        }
    ],
});

WDYT?

@posva
Copy link
Member

posva commented Jun 21, 2021

After further consideration and after revisiting the architecture of navigation guards, props cannot be async because they are a mere way to provide data to components and must be synchronous. They are not part of the navigation flow.

It's however easy to recreate a similar behavior in Vue Router 4 by storing data in to.meta in a navigation guard like beforeResolve() and then using it in props props: to => ({ data: to.meta.data }).
In Vue router 3, changes to to.meta should be avoided. It's doable but it will modify the original route record of the last entry in route.matched while in v4, each navigation creates a fresh meta property

@posva posva closed this as completed Jun 21, 2021
@stephane303
Copy link

@posva [In Vue router 3, changes to to.meta should be avoided. One could ...], you never finished this sentence, and I am using Vue router 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants