vuejs / vue-router

🚦 The official router for Vue 2
http://v3.router.vuejs.org/
MIT License
18.99k stars 5.06k forks source link

Allow nested routes without parent component #2105

Closed rightaway closed 6 years ago

rightaway commented 6 years ago

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.

posva commented 6 years ago

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: ... },
        ])
    ]
})
deckar01 commented 5 years ago

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: ... },
  ]
}]})
vipulw2011 commented 4 years ago

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.

GastonDonnet commented 4 years ago

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 "/"

andreasvirkus commented 4 years ago

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: ... },
  ]
}]})
oles commented 3 years ago

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:

asebold commented 3 years ago

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.

carmel commented 3 years ago

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

andreasvirkus commented 3 years ago

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.

carmel commented 3 years ago

@andreasvirkus Thank you, It's all right!

Jenesius commented 3 years ago

@carmel Hello dear team! Also u can use RouterView constructor to make ur code clean!

import {RouterView} from "vue-router";
export default [
    {
        path: "/import-permits",
        component: RouterView,
        children: [
            {
                path: "applications",
                component: RouterView,
                children: [
                    {
                        path: "list",
                        name: "list",
                        component: ViewIPApplicationsList
                    }
                ]
            }
        ]
    }
]
james-becker commented 3 years ago

@Jenesius While the above looks promising, I could not get it to work.

Jenesius commented 3 years ago

@Jenesius While the above looks promising, I could not get it to work.

What version of VUE u use?

azollai commented 3 years ago

I use vue-router 3.5.1 and I also don't have option to import RouterView. Would be nice, though.. image

Jenesius commented 3 years ago

@azollai VueRouter is available for vue-router 4.x. version (from vue-router-next / Vue3 )

dnikl commented 3 years ago

Is there a way to pass through multiple named router-views from a parent component to its children? In the example, there are children with and without a sideBar.

MicrosoftTeams-image (1)

MicrosoftTeams-image

marsimeau commented 3 years ago

We have been using component: RouterView, but now <transition> doesn't trigger between children routes. We are computing a key to get around that issue. However, this solution requires route related logic to be written in our Layout which isn't ideal.

Soren-Knudsen commented 2 years ago

@dnikl: Did you ever find a solution for this? I have precisely the same use case

dnikl commented 2 years ago

@Soren-Knudsen unfortunately not.

lifenautjoe commented 2 years ago

4 years later, I keep coming back to this issue...

anaselbahrawy commented 2 years ago

nested routes and nested names and meta group

function prefixRoutes(prefix, name, meta, routes) {
    return routes.map((route) => {

        route.path = '/' + prefix + '' + route.path;

        let named = name + '.' + route.name

        route.name = named.replace(/\.$/, "")

        Object.assign(route.meta, ...meta)

        return route;
    });
}

you can call function like ...prefixRoutes('admin', 'admin', [{AdminAuth: true}], []),

Mouvedia commented 2 years ago

Anyone found a clean solution to get rid of this warning :

[Vue Router warn]: <router-view> can no longer be used directly inside <transition> or <keep-alive>.

when using component: RouterView,?

Pinnokkio commented 11 months ago

Any news on this about how to add nested routes without parent component?