'How to inherit props from parent route with vue-router
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.
Solution 1:[1]
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:
- In
ParentComponent.vue, usetoRefs()onpropsto get a reactiverefto theidprop. - Use
provide()to provide thatidto any children (including child routes). - Apply a
keyon<router-view>to ensure the view is re-rendered based on the uniqueid. - In
ChildComponent.vue, use theinjectprop to inject theidfrom 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>
Solution 2:[2]
TL;DR See working demo
I don't clearly understand what you are asking, but here is my understanding of your questions:
- Is the route function called only once on the parent component and not on the children?
- How to not repeat props function code for every child route
- How to inherit props from parent component(s)
1: Nope, the route function will be called on each child route.
2: Abstract the function code
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
}
]
}
//...
3: Vue router passes down props
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 ?
// ...
}
Your Solution
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>
Other non-so-recommended solutions ?
- If you don't want to define props, you can access the id with
$attrs.idin the template. - Or you can use
useRoute()function from Vue Router and access params:
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
<template>
{{ route.params.id }}
</template>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Roland |
