6
votes

I have the following router config:

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'notselected',
      component: PackageUnselected
    },
    {
      path: '/package/:id',
      children: [
        { path: 'meta', name: 'packageMeta', component: ViewPackageMeta },
        { path: 'readme', name: 'packageReadme', component: PackageReadme },
        { path: 'docs', name: 'packageDocs', component: PackageDocs },
        {
          path: 'playground',
          name: 'packagePlayground',
          component: PackagePlayground
        }
      ]
    },
    {
      path: '/about',
      name: 'about',
      component: About
    },
    {
      path: '*',
      redirect: '/'
    }
  ]
});

And when I'm at the root route it correctly identifies the route name as notselected. When I route to any of the "/package/[id]" routes though it continues to load the PackageUnselected component instead of the appropriate route (aka, ViewPackageMeta, PackageDocs, etc.).

Now at the point in the DOM where I want the route to insert the route's component I have the following template:

<v-tab-item v-for="item in tabs" :id="'tab-item-' + item" :key="item" exact>
  item: {{item}}
  <router-view :selectedPackage="selected"></router-view>
</v-tab-item>

And because I have installed vuex-router-sync it's easy to see the route state at any given time. So when clicking on the route that should load PackageDocs:

enter image description here

But the component view window of vue-devtools looks like this:

enter image description here

the highlighted area shows that NO component has been loaded into the tabs. I then tried adding a component to the definition of the parent route /package/:id:

{
  path: '/package/:id',
  component: Packages,
  children: [
    { path: 'meta', name: 'packageMeta', component: ViewPackageMeta },
    { path: 'readme', name: 'packageReadme', component: PackageReadme },
    { path: 'docs', name: 'packageDocs', component: PackageDocs },
    {
      path: 'playground',
      name: 'packagePlayground',
      component: PackagePlayground
    }
  ]
},

I then had to create the world simplest component for Packages:

<template>
  <view-router-view></view-router-view>
</template>

This results in the following:

enter image description here

Hmmm. Can't figure out what to do next. Anyone have any pointers?

1
Have you read over the documentation on nested routes? Likely what you need is a single <view-router> outlet that displays the active tab, but the tabs loop is creating a separate outlet for each one. This reflects what you see in the dev tools.DigitalDrifter
Yeah I know I started with the default Vuetify way of using the component (which relies on v-model) and had planned on refactoring that later. I have refactored it now so that the VTabItem is removed and there is a single <view-router>. Still the overall problem is unchanged though.ken
I have gotten it to work but in an inelegant way ... I just created a number of root routes but I’d love to solve the problem in way more similar to how I started.ken
Are you familiar with dynamic components? I've found them to be, at least what I consider, a more elegant approach than having many routes.DigitalDrifter
Yeah I used them extensively in Ember and I remember I read about them in VueJS but I haven’t pulled that out of the toolkit yet. I’ll have a look, I can see how they might work better in many cases.ken

1 Answers

0
votes

When I route to any of the "/package/[id]" routes though it continues to load the PackageUnselected component instead of the appropriate route (aka, ViewPackageMeta, PackageDocs, etc.).

That is the correct behavior of the vue-router.

Children can only be loaded when URL paths are:

  • /package/[id]/meta
  • /package/[id]/readme
  • /package/[id]/playground
  • /package/[id]/docs

Parent path may not have component defined if you have configured redirect option and your user, who opens /package/[id] will be redirected to your default path (could be anything).

Lets move to the next part of your question...

<v-tab-item v-for="item in tabs" :id="'tab-item-' + item" :key="item" exact>
  item: {{item}}
  <router-view :selectedPackage="selected"></router-view>
</v-tab-item>

You don't need to create here 4 different <router-view> tags, you just need one where all your children components will display html code.

<v-tab-item v-for="item in tabs" :id="'tab-item-' + item" :key="item" exact></v-tab-item>
<router-view></router-view>

Now you will have only one router-view and it's the default one. When user clicks on any of your tabs you just need to this.$router.push a new path on @click-event in Packages component. That's it.

I have created a simple example (codepen) to demonstrate how this task can be solved:

Vue.use(VueRouter);

// Components
let MetaPackages = {
  mounted() { console.log('Mounted MetaPackages...'); },
  template: `<div>MetaPackages...</div>`,
};
let DocsPackages = {
  mounted() { console.log('Mounted DocsPackages...'); },
  template: `<div>DocsPackages...</div>`,
};
let ReadmePackages = {
  mounted() { console.log('Mounted ReadmePackages...'); },
  template: `<div>ReadmePackages...</div>`,
};
let Packages = {
  mounted() { console.log('Mounted Packages... ' + this.$route.path); },
  template: '<div>Packages (parent) screen...<br/><router-view></router-view></div>',
};

// Router
const router = new VueRouter({
  mode: 'hash',
  routes: [
    {
      path: "/packages/:id",
      component: Packages,
      children: [
        {path:"meta", component: MetaPackages},
        {path:"docs", component: DocsPackages},
        {path:"readme", component: ReadmePackages}
      ]
    }
  ]
});

// Vue instance
const vm = new Vue({
  el: '#app',
  router,
  components: {Packages, MetaPackages, DocsPackages, ReadmePackages}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.2/vue-router.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"> 
  <router-link to="/packages/100/meta">Meta</router-link>
  <router-link to="/packages/100/docs">Docs</router-link>
  <router-link to="/packages/100/readme">Readme</router-link>
  <hr/>
  <router-view></router-view>
</div>