Search code examples
vue.jsvue-router

Conditionally show view using Vue Router


So I'm trying to achieve a layout with optional sidebars based on the vue routes

Currently I have the following:

  1. an App.vue which has a MainLayout component with slots where I'm placing the router views
<main-layout>
    <template #main>
        <router-view/>
    </template>
    <template #sidebar>
        <router-view name="LeftSidebar"/>
    </template>
    <template #rightsidebar>
        <router-view name="RightSidebar"/>
    </template>
</main-layout>
  1. the router looking something like this:
const routes = [
  {
    path: "/lectures",
    components: {
      default: LectureList,
      LeftSidebar: MainMenu,
    }
  },
  {
    path: "/users",
    components: {
      default: UserList,
      LeftSidebar: MainMenu,
      RightSidebar: SomeOtherComponent
    }
  },
]
  1. the MainLayout.vue looking something like this (simplified)
<div>
    <div class="left-sidebar">
        <slot name="sidebar"/>
    </div>
    <div class="main">
        <slot/>
    </div>
    <div class="right-sidebar">
        <slot name="rightsidebar"/>
    </div>
</div>

Currently the main menu layout has a slot for the rightsidebar and I'd like it to render only if it has some content, ie if some route provides a component for the RightSidebar router view. I tried looking at the slots in MainLayout and add a v-if on the .right-sidebar div to only show if the slot for rightsidebar slot is present, but I guess it doesn't work since the router-view is present in there already

Any recommended way of dealing with this type of case?


Solution

  • Using the matched property of the Vue router named views, we can check which props have been passed or not.

    Here in the code, I added a watcher which will check for every route if props for sidebar views are passed or not and will update the data properties to used as the conditions.

    <main-layout>
       <template #main>
          <router-view/>
       </template>
       <template #sidebar v-if="isLeftSideBar">
          <router-view name="LeftSidebar"/>
       </template>
       <template #rightsidebar v-if="isRightSideBar">
          <router-view name="RightSidebar"/>
       </template>
    </main-layout>
    
    <script >
    export default {
        name: "App",
        data() {
            return {
                isLeftSideBar: false,
                isRightSideBar: false
            }
        },
        watch: {
            $route(to, from) {
                if (to.matched.length) {
                    this.isLeftSideBar = to.matched[0].props.hasOwnProperty("LeftSidebar");
                    this.isRightSideBar = to.matched[0].props.hasOwnProperty("RightSidebar");;
                }
            }
        },
    } 
          
    </script>
    

    Please let me know if this works.