Vue-router: Allow nested routes without parent component

Created on 14 Mar 2018  路  10Comments  路  Source: vuejs/vue-router

What problem does this feature solve?

Allows the routes to be defined with a prefix that doesn't need to be repeated in each route.

What does the proposed API look like?

Currently this doesn't work because the parent route doesn't have a component specified. This feature isn't about nested routes but just about nested paths.

{ path: '/prefix': children: [
  { path: 'one', component: ... },
  { path: 'two', component: ... },
]}

Creates /prefix/one and /prefix/two.

Most helpful comment

I hacked around this with a component named PassThrough that just contains a <router-view />. It should be possible to make this the default behavior when the component property is omitted from a route.

<!-- PassThrough.vue -->
<template>
  <router-view />
</template>
// router.js
new Router({routes: [{
  path: '/prefix',
  component: PassThrough,
  children: [
    { path: 'one', component: ... },
    { path: 'two', component: ... },
  ]
}]})

All 10 comments

If you need nested path the recommended approach is to use a function to generate the routes with the prefix:

function prefixRoutes(prefix, routes) {
    return routes.map(route => route.path = prefix + '/' + route.path)
}

new Router({
    routes: [
        // some routes
        ...prefixRoutes('/prefix', [
          { path: 'one', component: ... },
          { path: 'two', component: ... },
        ])
    ]
})

I hacked around this with a component named PassThrough that just contains a <router-view />. It should be possible to make this the default behavior when the component property is omitted from a route.

<!-- PassThrough.vue -->
<template>
  <router-view />
</template>
// router.js
new Router({routes: [{
  path: '/prefix',
  component: PassThrough,
  children: [
    { path: 'one', component: ... },
    { path: 'two', component: ... },
  ]
}]})

If you need nested path the recommended approach is to use a function to generate the routes with the prefix:

function prefixRoutes(prefix, routes) {
  return routes.map(route => route.path = prefix + '/' + route.path)
}

new Router({
  routes: [
      // some routes
      ...prefixRoutes('/prefix', [
        { path: 'one', component: ... },
        { path: 'two', component: ... },
      ])
  ]
})

How should specify path to direct? Just specifying without prefix or with both is taking me to 404.

If you need nested path the recommended approach is to use a function to generate the routes with the prefix:

function prefixRoutes(prefix, routes) {
  return routes.map(route => route.path = prefix + '/' + route.path)
}

new Router({
  routes: [
      // some routes
      ...prefixRoutes('/prefix', [
        { path: 'one', component: ... },
        { path: 'two', component: ... },
      ])
  ]
})

This throw a error because inside the map you return only the path.
The solution is:

function prefixRoutes(prefix, routes) {
  return routes.map((route) => {
    route.path = prefix + '' + route.path;
    return route;
  });
}

Example:

...prefixRoutes('/shop/:shopId', [
        {
          path: '/',
          name: 'Shop',
          component: () => import('../views/shop/Shop.vue'),
        },
        {
          path: '/category/:categoryId',
          name: 'Shop Products',
          component: () => import('../views/shop/Shop.vue'),
        },
      ]),

Routes return:

4: {path: "/shop/:shopId/", name: "Shop", component: 茠}
5: {path: "/shop/:shopId/category/:categoryId", name: "Shop Products", component: 茠}

You can change the union with prefix inside the function ("") to ("/") if you not start the children paths with "/"

Expanding on what @deckar01 proposed, we can do away with the redundant SFC 馃憞

new Router({routes: [{
  path: '/prefix',
  component: {
    // Inline declaration of a component that renders our <router-view>
    render: (c) => c('router-view'),
  },
  children: [
    { path: 'one', component: ... },
    { path: 'two', component: ... },
  ]
}]})

Just hit this issue again. I keep on forgetting this one on new projects where it makes sense to "namespace" several pages... and then I wonder why things broke, as there's just a blank page without error messages.

Then I re-read the docs, and read the thing with two <router-view></router-view> in https://router.vuejs.org/guide/essentials/nested-routes.html, and search for like "vue nested routes without main component", find this issue, and remember that I have to use the solution above.

It makes little sense to me that the child is not inserted into the main <router-view></router-view> when the parent has no component.

Also - hello, future me :wave:

I'd also like to give this issue a "bump".

Nested routes are particularly helpful for displaying a breadcrumb in my UI. Though the routes are nested, there's no point nesting components.

For example, in my app the path /leagues/:leagueId should render a completely different page than /leagues/:leagueId/teams/:teamId. At most, these pages share a top level component that serves as a base template. But the :teamId page should not inherit anything from the :leagueId page. They show completely different information and operate independently of each other. Currently I use the "pass through" option explained above, but creating a "dummy" component just to get the app to render correctly feels hacky.

It would be cleaner and less cumbersome if we didn't have to add a component to every route, and a child route was rendered in the component of it's closest ancestor with router-view.

Expanding on what @deckar01 proposed, we can do away with the redundant SFC 馃憞

new Router({routes: [{
  path: '/prefix',
  component: {
    // Inline declaration of a component that renders our <router-view>
    render: (c) => c('router-view'),
  },
  children: [
    { path: 'one', component: ... },
    { path: 'two', component: ... },
  ]
}]})

hi, @andreasvirkus. Do you know how to use it in next router of vue 3 ?
it will be throw TypeError with render: (c) => c('router-view')
image

log the 'c' out as follow:
image

I think you'd need to use Vue.h (docs: https://v3.vuejs.org/guide/render-function.html#render-functions)

import { h } from 'vue'
// or 
// import Vue from 'vue' // and use it as `Vue.h`

// router setup...
  render: () => h('router-view'),

I haven't tested this through yet though.

@andreasvirkus Thank you, It's all right!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rr326 picture rr326  路  3Comments

posva picture posva  路  3Comments

shinygang picture shinygang  路  3Comments

alexstanbury picture alexstanbury  路  3Comments

saman picture saman  路  3Comments