Vue-router: Resolving async data in router with async props

Created on 5 Dec 2017  路  9Comments  路  Source: vuejs/vue-router

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).

feature request

Most helpful comment

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

All 9 comments

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

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

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)

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.

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

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

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

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?

Was this page helpful?
0 / 5 - 0 ratings