Vue 3 Overview & Detail view same router-link should be active - vue.js

This nav structure is given:
<router-link to="/">Dashboard</router-link>
<router-link to="/travels">Travels</router-link>
...
Travels contains a list with all Travel Locations like so:
<div v-for="travel of travels" :key="travel.id">
<router-link :to="{name: 'Travel.Detail', params:{id:id}}">{travel.label}</router-link>
</div>
Router is working too:
{
path: '/',
name: 'Dashboard',
component: Dashboard
},
{
path: '/travels',
name: 'Travels.Overview',
component: () => import('#/views/projects/TravelsOverview.vue'),
},
{
path: '/travels/:id',
name: 'Travels.Detail',
component: () => import('#/views/projects/TravelsDetail.vue'),
}
A requirement now is that when selecting a travel and showing the TravelsDetail view that the menu item for Travels stays active. As far as i understand children / nested routes like so:
{
path: '/travels',
name: 'Travels.Overview',
component: () => import('#/views/projects/TravelsOverview.vue'),
children:[
{
path: ':id',
name: 'Travels.Detail',
component: () => import('#/views/projects/TravelsDetail.vue'),
}
]
}
Are only working WITHIN the parent view inside another tag and cannot be used to "replace" the parent correct?
So how it is possible to have such a structure with overview & detail views (full views) where in overview and detail view the link item still has the "active" class?

Okey i've found a solution here:
https://forum.vuejs.org/t/router-active-class-on-submenu-parent/7027
<router-link to="/travels" custom v-slot="{ href, navigate, isActive, isExactActive }">
<li :class="{ 'uk-active active': isActive || isExactActive || subIsActive('/travels') }">
<a :href="href" #click="navigate"><span>Travels</span></a>
</li>
</router-link>
And in the method:
subIsActive(input) {
const paths = Array.isArray(input) ? input : [input]
return paths.some(path => {
return this.$route.path.indexOf(path) === 0 // current path starts with this path string
})
}
Now every route that starts with /travels adds the active classes to the router-link item.

Related

Get children from current route

I have a vuejs 2 project and searching for a way to get the children of the current route (using typescript).
Wheather the this.$route nor the this.$router.currentRoute contains a children-prop.
is there any way?
With this information I would like to archive to generate tabs (subviews) automatically.
Update 1
Ok, the solution to get the child-routes is as follows:
this.$router.options.routes?.find((route) => route.name === this.$route.name)?.children
Now the challange is, that comming from a child root I first need to get the parent. Is there any way to also get the parent respectively the get the children of the root-Route?
So I need all children of the current or if available of the parent/root route.
Update 2
Ok, I currently came up with following solution:
this.$router.options.routes?.find((route) => route.name === this.$route.name || route.children?.find((child) => child.name === this.$route.name))?.children
if there is any cleaner/better solution please let me know.
Following my current solution:
<template>
<div class="router-tabs">
<div class="tabs">
<router-link v-for="(route, index) in childRoutes" :key="index" class="tab" :to="route">
{{ route.props.label }}
</router-link>
</div>
<router-view />
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
#Component
export default class RouterTabs extends Vue {
public get childRoutes() {
return this.$router.options.routes?.find((route) => route.name === this.$route.name || route.children?.find((child) => child.name === this.$route.name))?.children;
}
}
</script>
that works with following route-config:
{
path: '/tabs',
name: 'Tabs',
component: Tabs,
children: [
{
path: 'test',
name: 'Test-View',
component: Test,
props: { label: 'Test' }
},
{
path: 'test-two',
name: 'Test-View-Two',
component: TestTwo,
props: { label: 'Test12' }
}
]
},

How to display components of a child `router-view` that is nested within parent `router-view` in Vue JS?

I am trying to create a side menu (Dashboard's side menu with content) and I have to use multiple router-view.
First router-view is in App.vue that contains all the component.
<div id="app">
<router-view></router-view>
</div>
and the second router-view exists within above router-view as:
<div class="dashboard">
<Sidebar />
<div class="content">
<router-view name="content"></router-view>
</div>
</div>
According to below router/index.js code, if user visits / link, he will be redirected to dashboard page.
const routes = [
{
path: "",
name: "Dashboard",
component: dashboard,
},
{
path: "/messages",
name: "Messages",
components: {
content: Messages,
},
},
{
path: "/profile",
name: "Profile",
components: {
content: Profile,
},
},
{
path: "/settings",
name: "Settings",
components: {
content: Settings,
},
},
{
path: "/dashboard",
name: "Overview",
components: {
content: Overview,
},
},
];
and within above Sidebar component, there're links that upon clicking it shows the content of that link on right side (as in dashboard):
<div class="sidebar">
<div class="title">
Simple Sidebar
</div>
<div class="menu-items">
<router-link to="" active-class="active" tag="button" exact class="side-btn">
<div class="link-container">
Overview
</div>
</router-link>
<router-link to="/messages" active-class="active" tag="button" exact class="side-btn">
<div class="link-container">
Messages
</div>
</router-link>
// ....
</div>
</div>
for the second router-view I added a name as :
<router-view name="content"></router-view>
and then I identified it in router/index.js as:
{
path: "/messages",
name: "Messages",
components: {
content: Messages,
},
},
but it didn't work.
Can anyone help me debug and solve this issue, thank you in advance.
The nested route requires children route configs, which is missing from Dashboard's route config.
Move the route config of Messages into Dashboard's children array:
const routes = [
{
path: "",
name: "Dashboard",
component: dashboard,
children: [
{
path: "/messages",
name: "Messages",
components: {
content: Messages,
},
},
]
},
//...
]

VueJS - How to highlight the sidebar button even when displaying the router-view content

Hello I am highlighting the button which ever is clicked in the sidebar.
Here i am looking to highlight the button even if the router-view content is displayed. But currently below image is displayed if i am in router view content.
I have a component called Blog.vue, and the Blog button is placed in the sidebar. This Blog.vue contains several cards in it and each card has a badge, so when clicked any of the badge it displays that respective content here route changes.
Initially when the page loads or when clicked on Blog button in sidebar it is highlighted. But as soon as the user clicks any of the badge in any of the card then the Blog button is not highlighted shown in second image.
Here is the VerticalNavMenu.vue
<vs-sidebar
class="v-nav-menu items-no-padding"
v-model="isVerticalNavMenuActive"
ref="verticalNavMenu"
default-index="-1"
:click-not-close="clickNotClose"
:reduce-not-rebound="reduceNotRebound"
:parent="parent"
:hiddenBackground="clickNotClose"
:reduce="reduce"
v-hammer:swipe="onMenuSwipe">
<div #mouseenter="mouseEnter" #mouseleave="mouseLeave">
<div #mouseenter="mouseEnter" #mouseleave="mouseLeave">
<div class="shadow-bottom" v-show="showShadowBottom" />
<!-- Menu Items -->
<component :is="scrollbarTag" ref="verticalNavMenuPs" class="scroll-area-v-nav-menu pt-2"
:settings="settings" #ps-scroll-y="psSectionScroll" #scroll="psSectionScroll" :key="$vs.rtl">
<template v-for="(item, index) in menuItemsUpdated">
<!-- Group Header -->
<span v-if="item.header && !verticalNavMenuItemsMin" class="navigation-header truncate"
:key="`header-${index}`">
{{ $t(item.i18n) || item.header }}
</span>
<!-- /Group Header -->
<template v-else-if="!item.header">
<!-- Nav-Item -->
<v-nav-menu-item
...
</v-nav-menu-item>
<!-- Nav-Group -->
<template v-else>
<v-nav-menu-group
...
/>
</template>
<!-- /Nav-Group -->
</template>
</template>
</component>
<!-- /Menu Items -->
</div>
..
showShadowBottom: false
..
psSectionScroll () {
const scroll_el = this.$refs.verticalNavMenuPs.$el || this.$refs.verticalNavMenuPs
this.showShadowBottom = scroll_el.scrollTop > 0
},
Here is the navMenuItem.js:
{
header: 'Apps',
icon: 'PackageIcon',
i18n: 'Apps',
items: [
{
url: '/apps/blog-complete',
name: 'Blog',
slug: 'blog-complete',
icon: 'CpuIcon',
i18n: 'Blog'
},
{
url: '/apps/info-tech',
name: 'Info Tech',
slug: 'info-tech',
icon: 'Icon'
i18n: 'Info Tech'
},
...
]
Here is the router.js
{
path: '/apps/blog-complete',
name: 'app-blog-complete',
component: () => import('#/views/apps/Blog.vue'),
},
{
path: '/apps/inner-blog/:apiVal_id', // Here for these router view contents i want to show the button highlighted.
name: 'Inner',
component: () => import('./views/apps/InnerBlog.vue'),
},
Here is the Blog.vue:
...
<div class="right">
<b-badge #click="$router.push({name: 'Inner', params: {id: apiVal.id.toString() }})
.catch(err => {})">Check Deep</b-badge>
</div>
...
Here is the InnerBlog.vue:
...
<div class="right">
Welcome to my route :)
</div>
...
Please do help me to highlight the Blog button in the sidebar even when it displays the router-view content.
Easiest way to do this would be to utilize .router-link-active class which is added to the <router-link> component when its target route is matched, but since you are not using that it can be done using this trick:
First, add this to your methods:
methods: {
subIsActive(input) {
const paths = Array.isArray(input) ? input : [input]
return paths.some(path => {
return this.$route.path.indexOf(path) === 0 // current path starts with this path string
})
}
}
This function checks if the current path starts with whatever you passed to the function.
Second, you need to add class binding to your span element which is rendering group headers like this:
<span v-if="item.header && !verticalNavMenuItemsMin" class="navigation-header truncate"
:key="`header-${index}`" :class="{'is-active': subIsActive(item.url)}">
{{ $t(item.i18n) || item.header }}
</span>
Third, you need to change your routes, every badge that is available when you click on the Blog button should have a route that starts with /apps/blog-complete, for example
{
path: '/apps/blog-complete/inner-blog/:apiVal_id', // Here for these router view contents i want to show the button highlighted.
name: 'Inner',
component: () => import('./views/apps/InnerBlog.vue'),
},
Fourth, you add class is-active to your style section:
.is-active{
background-color: red;
}

vue-router Route with name 'ROUTENAME' does not exist in vuejs

I have some routes which are newly added. the sidebar is dynamically created based on links added to the routes.
I am able to print the route name in plain text but when assigned to the vue-route it simple gives localhost:8080 so where am i going wrong.
configroutes file:
const routes = [
{
path: 'create_schedule',
name: 'activate.create_schedule',
meta: {
_routeName: 'activate_create_schedule',
sectionName: 'Create Schedule'
},
component: createSchedule,
},
]
Main Routes File
import ConfigureRoutes from './configureRoutes.js';
const routes = [
...ConfigureRoutes,
];
export default routes;
export const getActivateConfigRoutes = function () {
return routes;
};
dashboard Component file
data() {
const configRoutes = getActivateConfigRoutes();
const sidebarRoutes = [
{
name: '/',
meta: {
sectionName: 'CONFIGURE'
},
redirect: {
name: 'activate.create_schedule'
},
children: [
...configRoutes
]
},
];
return {
sidebarRoutes
};
}
}
</script>
aside bar:
<aside class="menu">
<ul class="menu-list --campaign-sidebar">
<li class="main-section-menu" v-for="(sidebarRoute, index) in sidebarRoutes" :key="sidebarRoute.id">
<router-link :to="{ name: sidebarRoute.name, params: { campaign_id: currentCampaign.id }}" class="sidebar-link" active-class="is-active">
{{ sidebarRoute.meta.sectionName }}
</router-link>
<ul class="sub-menu-list" v-if="sidebarRoute.children.length > 0">
<li v-for="childRoute in sidebarRoute.children" :key="childRoute.id">
<router-link :to="{ name: childRoute.name , params: { campaign_id: currentCampaign.id }}" class="sidebar-sub-link" active-class="is-active-submenu router-link-active">
{{ childRoute.meta.sectionName }} {{ childRoute.name }}
</router-link></li>
</ul>
<span class="base" v-if="((sidebarRoutes.length - 1) === index)"></span>
</li>
</ul>
</aside>
you can see what when I try to print childRoute.name, it gives me the name and so that data is passed properly to the loop. then what is the issue here ? can someone help on the same ?
[vue-router] Route with name 'activate.create_schedule' does not exist vue-router.esm.js:16

How to exhange a full route? Router-link exchanges only the last part of the route

I am new to vue and vue router and am using Vue router in history mode. The app contains a menu which is dynamically loaded
<router-link
tag="li"
class="link"
v-for="item in menu"
v-bind:key="item.id"
:to="item.slug"
:exact="item.slug === '/' ? true : false">{{ item.content }}
</router-link>
It works well as long as I stay in the parent routes like http://localhost:8080/posts As soon as I navigate a level deeper, e.g. to a post with the id 8 http://localhost:8080/posts/8 with a routerlink inside the Template
<router-link
tag="h2"
class="link"
:to="{ name: 'post', params: { id: post.id }}">
{{ post.title.rendered }}
</router-link>
it works in one way, but doesnt go back to the parent route when I click the main navigation links. Instead just adds the link of the main menu to the end of the route, e.g. instead of http://localhost:8080/posts
http://localhost:8080/posts/posts
The router
const router = new Router({
mode: 'history',
base: '',
routes: [
{ path: '/', name: 'home', component: HomePage },
{ path: '/posts', name: 'posts', component: PostsPage },
{ path: '/posts/:id', name: 'post', component: SinglePost },
{ path: '/projects', name: 'projects', component: ProjectsPage },
{ path: '/projects/:id', name: 'project', component: ProjectPage },
{ path: '/:page', name: 'page', component: SinglePage },
{ path: "*", redirect: '/' },
],
// etc..
});
I guess I am making a common mistake, but I canĀ“t find a solution. Any help appreciated.
You can use absolute paths. Instead of
<router-link
tag="h2"
class="link"
:to="{ name: 'post', params: { id: post.id }}">
{{ post.title.rendered }}
</router-link>
use
<router-link
tag="h2"
class="link"
:to="{ name: '/post', params: { id: post.id }}">
{{ post.title.rendered }}
</router-link>
The change is /post vs post in router-link's :to attribute.
You could and probably should use nested routes and components for posts posts/:id, but this a little more work and you my not need it at the moment. More on nested routes here.