Tuesday, June 4, 2024
-1
rated 0 times [  5] [ 6]  / answers: 1 / hits: 18043  / 5 Years ago, fri, january 25, 2019, 12:00:00

I'm trying to have the navigation on my Vue app to have a main menu up top populated by all the root level routes, and a side menu if the route has any children. Like so:



Goal



The current Design I have has the main navigation with routing links on the top with the router-view below. I have been able to get the side menu working so it it only shows up when I choose Travellers and updates the components/content correctly. However I'm having trouble routing things correctly, when I click on one of the links in the sub menu it does not append to the current path. So when I click on View when I'm in localhost/Traveler and click View the url changes to localhost/View/ instead of localhost/Traveler/View. Also the selection on the top menu gets unselected when I choose something in the child menu.



Unselecting



And I cannot get to the pages via something like localhost/Traveler/View only localhost/View



I started rereading the documentation on nested routes as I began making this post and I think I realized that I should be creating an new router at each level which is not something I have done in my code below.



I'm also not sure how to access the children of the current route. I've tried to display them like so:



  <h2>Route: {{ $route.name }}</h2>
<ul id=example-1>
<li v-for=child in $route.children>
{{ child.name }}
</li>
</ul>


But I get nothing. Should they be passed as Params or something? Or are the not that easily accessible?



Any advice or help will be greatly appreciated.



Root



Contains top Menu-Nav with router links and router-view below.



<template>
<div id=app class=container-fluid>
<div class=row>
<div style=width:100%>
<nav-menu params=route: route></nav-menu>
</div>

</div>

<div class=row>
<div>
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import NavMenu from './nav-menu'

export default {
components: {
'nav-menu': NavMenu
},

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


Top Nav-Menu



Gets populated with the routes



<template>
<nav class=site-header sticky-top py-1>
<div class=container d-flex flex-column flex-md-row justify-content-between>
<a class=nav-item v-for=(route, index) in routes :key=index>
<router-link :to={path: route.path, params: { idk: 1 }} exact-active-class=active>
<icon :icon=route.icon class=mr-2 /><span>{{ route.display }}</span>
</router-link>
</a>
</div>
</nav>
</template>
<script>
import { routes } from '../router/routes'

export default {
data() {
return {
routes,
collapsed: true
}
},
methods: {
toggleCollapsed: function (event) {
this.collapsed = !this.collapsed
}
}
}
</script>


Traveler Page/View



Currently the Traveller Page which has a side bar menu and another router view for the content:



<template>
<div id=app class=container-fluid>
<div class=wrapper>
<traveler-menu params=route: route></traveler-menu>

<div id=content>
<router-view name=travlerview></router-view>
</div>
</div>
</div>
</template>
<script>
import TravelerMenu from './traveler-menu'
export default {
components: {
'traveler-menu': TravelerMenu
},
data() {
return {}
}
}
</script>


Side Bar/ Traveler Menu



<template>
<nav id=sidebar>
<div class=sidebar-header>
<h3>Route's Children:</h3>
</div>

<ul class=list-unstyled components>
<li>
<a class=nav-item v-for=(route, index) in travelerroutes :key=index>
<router-link :to={path: route.path, params: { idk: 1 }} exact-active-class=active>
<icon :icon=route.icon class=mr-2 /><span>{{ route.display }}</span>
</router-link>
</a>
</li>
</ul>

</nav>

</template>

<script>
import { travelerroutes } from '../../router/travelerroutes'
export default {
data() {
console.log(travelerroutes);
return {
travelerroutes,
collapsed: true
}
},
methods: {
toggleCollapsed: function (event) {
this.collapsed = !this.collapsed
}
}
}
</script>


Routes



import CounterExample from 'components/counter-example'
import FetchData from 'components/fetch-data'
import HomePage from 'components/home-page'
import TestPage from 'components/test-page'
import Travelers from 'components/Traveler/traveler-root'
import { travelerroutes } from './travelerroutes'

export const routes = [
{ name: 'home', path: '/', component: HomePage, display: 'Home', icon: 'home' },
{ name: 'counter', path: '/counter', component: CounterExample, display: 'Counter', icon: 'graduation-cap' },
{ name: 'fetch-data', path: '/fetch-data', component: FetchData, display: 'Fetch data', icon: 'list' },
{ name: 'test-page', path: '/test-page', component: TestPage, display: 'Test Page', icon: 'list' },
{
name: 'traveler-root', path: '/traveler', component: Travelers, display: 'Travelers', icon: 'list', children: travelerroutes
}
]


Traveler Routes (travelerroutes.js)



import TestPage from 'components/test-page'
import ViewTravelers from 'components/Traveler/TravelerPages/view-travelers'

export const travelerroutes = [{
name: 'View',
path: '/View',
display: 'View', icon: 'list',
components: {
travlerview: TestPage
}
},
{
name: 'Create',
path: '/Create',
display: 'Create', icon: 'list',
components: {
travlerview: ViewTravelers
}
},
{
name: 'Edit',
path: '/Edit',
display: 'Edit', icon: 'list',
components: {
travlerview: ViewTravelers
}
}];


router/index.js



import Vue from 'vue'
import VueRouter from 'vue-router'
import { routes } from './routes'

Vue.use(VueRouter)

let router = new VueRouter({
mode: 'history',
routes
})

export default router


app.js



import Vue from 'vue'
import axios from 'axios'
import router from './router/index'
import store from './store'
import { sync } from 'vuex-router-sync'
import App from 'components/app-root'
import { FontAwesomeIcon } from './icons'

// Registration of global components
Vue.component('icon', FontAwesomeIcon)

Vue.prototype.$http = axios

sync(store, router)

const app = new Vue({
store,
router,
...App
})

export {
app,
router,
store
}


Let me know if you you need anymore details, context or code.


More From » user-interface

 Answers
18

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>




[#52710] Tuesday, January 22, 2019, 5 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
amberlykaliac

Total Points: 415
Total Questions: 100
Total Answers: 85

Location: Wallis and Futuna
Member since Tue, Mar 30, 2021
3 Years ago
amberlykaliac questions
Mon, Sep 28, 20, 00:00, 4 Years ago
Wed, Jul 29, 20, 00:00, 4 Years ago
Sun, Jul 12, 20, 00:00, 4 Years ago
Sun, May 17, 20, 00:00, 4 Years ago
;