I have the following router configuration and would like that the id
of the main.parent
route is converted to an integer and then passed into the child components (given props: true
) for all child components.
{
path: '/',
component: GrandparentComponent,
name: 'main',
children: [{
path: ':id',
component: ParentComponent,
name: 'main.parent',
props: route => {
return {
// parse URL id to int
id: parseInt(route.params.id, 10)
};
},
children: [{
path: 'details',
name: 'main.parent.child',
component: ChildComponent,
props: true,
children: [{
...
}]
}]
}]
}
However, it seems as if the route function is only really called once (when evaluating /:id
) and not when /:id/details
is evaluated. The relevant code in vue-router seems to confirm this.
const route = routeToDisplay.value;
const matchedRoute = matchedRouteRef.value;
const ViewComponent = matchedRoute && matchedRoute.components[props.name];
// we need the value at the time we render because when we unmount, we
// navigated to a different location so the value is different
const currentName = props.name;
if (!ViewComponent) {
return normalizeSlot(slots.default, { Component: ViewComponent, route });
}
// props from route configuration
const routePropsOption = matchedRoute.props[props.name];
const routeProps = routePropsOption
? routePropsOption === true
? route.params
: typeof routePropsOption === 'function'
? routePropsOption(route)
: routePropsOption
: null;
...
const component = h(ViewComponent, assign({}, routeProps, attrs, {
onVnodeUnmounted,
ref: viewRef,
}));
...
I wonder if anyone tried to solve the same problem and what they came up with a solution. Ideally, I'd like to avoid duplicating the props function for every child route.
There's no such feature in Vue Router for passing props to child routes that way.
Instead, you could use provide
/inject
to effectively do this. The parent would provide
the id
property, and any descendant could inject
it:
ParentComponent.vue
, use toRefs()
on props
to get a reactive ref
to the id
prop.provide()
to provide that id
to any children (including child routes).key
on <router-view>
to ensure the view is re-rendered based on the unique id
.ChildComponent.vue
, use the inject
prop to inject the id
from step 2.// ParentComponent.vue
<script>
/* Composition API */
import { provide, toRefs } from 'vue'
export default {
props: ['id'],
setup(props) {
const { id } = toRefs(props)
provide('id', id)
},
}
/* Options API */
import { toRefs } from 'vue'
export default {
props: ['id'],
provide() {
const { id } = toRefs(this.$props)
return { id }
},
}
</script>
<template>
<router-view :key="id" />
</template>
// ChildComponent.vue
<script>
export default {
inject: ['id'],
}
</script>
<template>
<h2>Detail {{ id }}</h2>
</template>
I don't clearly understand what you are asking, but here is my understanding of your questions:
Instead of copy-pasting or repeating the function code, you can create a function and use that:
const parseUrlIdtoInt = (route) => ({ id: parseInt(route.params.id, 10) });
// ...
{
path: '...'
props: parseUrlIdtoInt,
children: [
{
path: '...',
props: parseUrlIdtoInt
}
]
}
//...
If the props
property is specified in the router configuration, then Vue Router automatically passes down the props.
- With props option we enable passing props to the component - With function props we control how and what prop to pass
// Router configuration
{
path: '...',
name: '...',
props: true // or a function 👈
// ...
}
So in order to access id
prop in your ChildComponent
you can define props and use it:
Composition API
<script setup>
defineProps({ id: Number });
</script>
Options API
<script>
import { defineComponent } from 'vue';
export default defineComponent({
props: {
id: Number,
},
});
</script>
$attrs.id
in the template.useRoute()
function from Vue Router and access params:<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
<template>
{{ route.params.id }}
</template>