John Willemse
John Willemse

Reputation: 6698

Vue-router named views not working as expected

I'm stuck on getting my Vue-router to work as I want. I have two <route-view /> Named Views in my templates. One is the main view for navigation on the top nav bar, which is working fine. The other doesn't work. I have defined my router as such (shortened for clarity):

import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';

const routes = [
  {
    path: '/',
    name: 'home',
    components: { main: HomeView },
  },
  {
    path: '/bpvapp',
    name: 'bpvapp',
    components: { main: () => import('../views/BpvApp.vue') },
  },
  {
    path: '/bpvapp/projects',
    name: 'projects',
    components: { bpvapp: HomeView }, // HomeView component for testing purposes
},
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;

My App.vue has the main route defined in the template:

<template>
  <div class="main-container">
    <navigation-bar
      :isLoggedIn="isLoggedIn"
      :profileImageUrl="userImage"
      :signOut="signOut"
    />
    <suspense>
      <router-view name="main" />
    </suspense>
  </div>
</template>

My BpvApp.vue component, which is a CSS Grid layout, uses the second route view (shortened for clarity):

<template>
  <div class="bpv--app-container">
    <div class="bpv--app-toolbar">TOOLBAR</div>
    <div class="bpv--app-sidebar">
      <bpv-button
        text="Projects"
        icon="folder-star-multiple"
        styling="info"
        class="bpv--bs-1"
        :active="activeSection === 'projects'"
        to="/bpvapp/projects"
      />
    </div>
    <div class="bpv--app-content">
      <suspense>
        <router-view name="bpvapp" />
      </suspense>
    </div>
    <div class="bpv--app-assist">
      RIGHT Lorem ipsum dolor sit amet consectetur, adipisicing elit. Laboriosam
      placeat deserunt quidem fugiat dicta repellat nobis mollitia! Consectetur
      laudantium dolor, odio adipisci at qui, deserunt minus facere rerum,
      voluptates maiores.
    </div>
    <div class="bpv--app-footer">FOOTER</div>
  </div>
</template>

The div with class bpv--app-content should show the content from the component I push to the router. You can see the button will push to /bpvapp/projects, which in my router definition should invoke the router with name bpvapp and show the content.

For the sake of completeness, my button component handles the to prop as follows:

    ...
    methods: {
      // this.to has the correct value '/bpvapp/projects', I double-checked
      buttonClicked(e) {
        if (this.to) {
          this.$router.push(this.to);
        } else if (this.action) {
          this.action();
        } else {
          this.$emit('click', e);
        }
      },
    },
    ...

What happens now is that I get a blank screen. When I inspect the HTML, the entire structure is not there, except the nav bar.

What am I doing wrong here? I've tried removing the <suspense> tag and loading static content, but that made no difference.

Upvotes: 0

Views: 2297

Answers (1)

tony19
tony19

Reputation: 138196

One issue is the two routes are configured as siblings, but the second one should be a nested child route of the first, using the route's children option:

// router.js
const routes = [
  {
    path: '/',
    name: 'home',
    components: { main: HomeView },
  },
  {
    path: '/bpvapp',
    name: 'bpvapp',
    components: { main: () => import('@/views/BpvApp.vue') },

       👇
    children: [
      {
        path: 'projects',
        name: 'projects',
        components: { bpvapp: () => import('@/views/BpvProject.vue') },
      },
    ],
  },
]

Note: When there's only one router-view at a particular level, you don't need to name it.

The other issue is you don't need <suspense> for this. <suspense> is intended for async components declared with an async setup() option or within defineAsyncComponent() (which is not appropriate for a route's components option). While the router components are technically loaded asynchronously via the dynamic imports, they're not actually the async components that <suspense> expects.

demo

Upvotes: 2

Related Questions