How to implement sub menu of the current route's children with vue.js and vue router

谁说我不能喝 提交于 2019-12-05 10:32:37

You don't need to create new router instances, instead watch the $route property and create the sidebar nav menu as it changes. You'll need to pull the child routes from the $router.options.routes. Here's an example:

const router = new VueRouter({
  routes: [{
      name: 'home',
      path: '/',
      component: {
        template: '<div>Home</div>'
      }
    },
    {
      name: 'foo',
      path: '/foo',
      component: {
        template: '<div>Foo<router-view></router-view></div>'
      },
      children: [{
        name: 'foo.baz',
        path: '/baz',
        component: {
          template: '<div>Baz</div>'
        }
      }, {
        name: 'foo.tar',
        path: '/tar',
        component: {
          template: '<div>Tar</div>'
        }
      }]
    },
    {
      name: 'bar',
      path: '/bar',
      component: {
        template: '<div>Bar<router-view></router-view></div>'
      },
      children: [{
        name: 'bar.zim',
        path: '/zim',
        component: {
          template: '<div>Zim</div>'
        }
      }, {
        name: 'bar.zug',
        path: '/zug',
        component: {
          template: '<div>Zug</div>'
        }
      }]
    }
  ]
})

new Vue({
  el: '#app',
  router,
  data() {
    return {
      children: []
    }
  },
  watch: {
    $route: function(current) {
      const route = this.$router.options.routes.find(route => route.path === current.path)

      if (route && Array.isArray(route.children)) {
        this.children = route.children
      } else if (route) {
        this.children = []
      }
    }
  }
})
* {
  margin: 0;
  padding: 0;
}

html,
body,
#app {
  width: 100%;
  height: 100%;
}

#top {
  border: 1px solid black;
}

#top ul {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  list-style-type: none;
}

li {
  padding: 1rem;
  text-align: center;
  text-transform: uppercase;
}

li a {
  text-decoration: none;
  font-weight: bold;
  color: black;
}

#sidebar {
  height: 50%;
  width: 100px;
  border: 1px solid black;
}

#content {
  width: 50%;
}

#content,
#content>div {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
  <div id="top">
    <ul>
      <li v-for="item in $router.options.routes" :key="item.path" :style="{ 'background-color': $route.name.includes(item.name) ? 'rgba(197,225,165,1)' : 'white' }">
        <router-link :to="item.path">{{ item.name }}</router-link>
      </li>
    </ul>
  </div>
  <div style="display: flex; flex-direction: row; height: 100%; width: 100%;">
    <div id="sidebar">
      <ul>
        <li v-for="item in children" :key="item.path" :style="{ 'background-color': $route.name === item.name ? 'rgba(197,225,165,1)' : 'white' }">
          <router-link :to="item.path">{{ item.name.split('.').pop() }}</router-link>
        </li>
      </ul>
    </div>
    <div id="content">
      <router-view></router-view>
    </div>
  </div>
</div>

I made some modifications to DigitalDrifter's answer where I find the children using the template syntax. I'm pretty sure my answer will stop working after another level of children so I'm going to say DigitalDrifter still has a better answer.

Also I'm not sure how preserve the sub menues route (localhost/Traveller) so it isn't searching the children of (locathost/Traveller/View) when I click on something like view. Right now I'm doing it in kind of a janky way:

v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.path||$route.path.includes(x.path))"

Thet full SFC without styling is here:

<template>
  <nav id="sidebar">
    <div class="sidebar-header">
      <h3>Bootstrap Sidebar</h3>
    </div>

    <ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.path||$route.path.includes(x.path))">
      <li v-for="child in route.children">
        <a class="nav-item" :key="index">
          <router-link :to="{path: route.path+'/'+child.path}" exact-active-class="active">
            <icon :icon="route.icon" class="mr-2" /><span>{{ child.path }}</span>
          </router-link>
        </a>
      </li>
    </ul>

  </nav>

</template>

<script>
  export default {
    data() {
      return {
      }
    },
    methods: {
    }
  }
</script>

Edit: I know vue has the data I want associated with the routers views as shown here,

I'm just not sure how to access those properties.

Update 1/29/2019:

I figured out that all you need to do to get the path is by using $Route.matched[0].path you can learn more here.

Now I have been able to simplify the menu into an SFC like this:

<template>
    <nav id="sidebar">
      <div class="sidebar-header">
        <h3>Bootstrap Sidebar</h3>
      </div>

      <ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.matched[0].path)">
        <li v-for="child in route.children">
          <a class="nav-item"  :key="index">
            <router-link :to="route.path+'/'+child.path" exact-active-class="active">
              <icon :icon="route.icon" class="mr-2" /><span>{{ child.name }}</span>
            </router-link>
          </a>
        </li>
      </ul>

    </nav>

</template>

<script>
    export default {
    data() {
        return {
        }
      }
    }
</script>

CSS not included.

Edit: I now made this my preferred answer because it allows me to use the side menu anywhere by just referencing the component. I don't need to pass anything into it and it should work anywhere. I do like digitaldrifter's answer though because he has a fiddle for it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!