How to keep the active link in a non-exact route with vue-router - vue.js

I have an application with Vue and VueRouter, in my root component I have a tab panel, each tab is a router-link, when I click on each routerlink it takes me to the specific route, until then everything is fine, for example the route misitio.com/shop/ activate the link for this route
the problem is when within that route there is another component that leads to mysitio.com/shop/categories, I would like to leave the link of the route mysite.com/shop active but it is not like that.
Example Code
<div class="col-md-12">
<ul class="nav nav-tabs md-tabs nav-justified default-color-dark" role="tablist">
<li class="nav-item ">
<router-link :to="{ name: 'shop'}" exact
class="nav-link text-white"
data-toggle="tab"
role="tab">
<i class="fa fa-table"></i> SHOP
</router-link>
</li>
<li class="nav-item">
<router-link :to="{ name: 'other'}" exact
class="nav-link text-white"
data-toggle="tab"
role="tab" >
<i class="fa fa-heart"></i> OTHER
</router-link>
</li>
</ul>
<div class="tab-content card">
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</div>
</div>
The urls of my tabs are
midominio.com/shop/
midominio.com/shop/categories/
if I do not use exact, when I select the second option the first one also marks me
How can I achieve this behavior? I thought of a regular expression maybe to activate the link

Probably not the best solutions, but these are the ways that your problem could be solved:
controlling the active class like v-bind:active-class="$route.path
!== '/shop/categories/' ? '(your active class)' : ''
control the exact prop like v-bind:exact="$route.path ===
'/shop/categories/'"

Related

Wrapper element for for loop

I'm currently expanding my horizon by trying out Vuejs.
I'm creating a navigation with data coming from my Vue instance, and I have the following code:
<ul class="nav">
<li class="nav-item" v-for="navLink in navLinks" :key="navLink.id">
<a class="nav-link" :href="navLink.url">{{ navLink.name }}</a>
</li>
</ul>
Now even though this works perfectly fine, I have seen the following two examples in videos instead:
<ul class="nav">
<template v-for="navLink in navLinks">
<li class="nav-item">
<a class="nav-link" :href="navLink.url">{{ navLink.name }}</a>
</li>
</template>
</ul>
<ul class="nav">
<div v-for="navLink in navLinks" :key="navLink.id">
<li class="nav-item">
<a class="nav-link" :href="navLink.url">{{ navLink.name }}</a>
</li>
</div>
</ul>
I'm asking myself which one of these is best practice.
Now from what I've learned, one should always (if possible) set a :key in a for loop. This is not possible on the tag, therefore I would think this option is the worse.
But what about option 3 and mine? Is there any difference? Is it just personal preference or is there an actual reason on why to choose one or the other.
Ty
The only difference I spot between the first and the third options is that you'd wrap each list item in an additional div. You don't need those wrappers unless you want to use this nesting for styling purposes.
Your example is perfectly fine. Just make sure ids you use for keys are unique.
Also avoid using v-if and v-for on the same element and rather add v-if on the parent
I think you should always put a key property in your for loop.
Say you have nothing to work with for an ID (some item.name could potentially be duplicated?) just use this:
<div v-for="(item, index) in items)" :key="index">
{{ item.name }}
</div>
index in this case is just the index of the items array.
So yours will be rendered like:
<ul class="nav">
<li class="nav-item" :key="navLink.id">
<a class="nav-link" :href="navLink.url">{{ navLink.name }}</a>
</li>
...
<li class="nav-item" :key="navLink.id">
<a class="nav-link" :href="navLink.url">{{ navLink.name }}</a>
</li>
</ul>
Whereas the third one will be:
<ul class="nav">
<div :key="navLink.id">
<li class="nav-item">
<a class="nav-link" :href="navLink.url">{{ navLink.name }}</a>
</li>
</div>
...
<div :key="navLink.id">
<li class="nav-item">
<a class="nav-link" :href="navLink.url">{{ navLink.name }}</a>
</li>
</div>
</ul>
I would say it's a bit verbose to wrap a <li> inside a <div> in each one of them.
Yours is fine, all of them indeed are going to work well, but the third one may have problems with custom css, since it's a wrapper for the tag.
The template is an alternative as it is stated in the docs.
You've set the key at the li tag, as long as you're following the guide for mantaining state with keys, it's fine.

How to apply a class according to the route in Vue

I'm trying to switch the Class accordingly to the route, but it works for the 2 of them but the first one keep the Class active.
App.vue
<ul class="nav nav-tabs">
<li class="nav-item">
<router-link tag="a" class="nav-link" to="/" active-class="active">
Home
</router-link>
</li>
<li class="nav-item">
<router-link tag="a" class="nav-link" to="about" active-class="active">
About
</router-link>
</li>
<li class="nav-item">
<router-link tag="a" class="nav-link" to="form" active-class="active">
Form
</router-link>
</li>
</ul>
Use exact-active-class instead of active-class in your router-link elements.

VueJS: Dropdown on navbar doesn't work while navigate to other page

I have a Vue single-page-apps about a simple online bookstore. This project so far runs well except a tiny issue: When a login customer clicks 'logout' link on navbar, the system navigates him to the frontpage and 'logout' link becomes a 'register/login' link. This link should be a dropdown, but after navigating, this link doesn't work(no respond while clicking it) anymore until press F5 to reload the page from 'server'. Following is my navbar.vue
<template>
...
<!---Good Dropdown--->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownShowMenuLink"
role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Showroom
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownShowMenuLink">
<router-link class="dropdown-item" to="/showBookList/newAddings">By Updates</router-link>
<router-link class="dropdown-item" to="/showBookList/authors">By Authors</router-link>
<router-link class="dropdown-item" to="/showBookList/categories">By Categories</router-link>
</div>
</li>
<template v-if="isLogin">
<li class="nav-item">
<a class="nav-link" id="logoutLink" href="javascript:void(0)" #click="logout">logout</a>
</li>
<template v-if="isAdmin">
<li class="nav-item">
<router-link class="nav-link" to="/admin">Dashboard</router-link>
</li>
</template>
</template>
<template v-else>
<!--Bad dropdown-->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" id="navbarDropdownUserMenuLink"
role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Login/Register</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownUserMenuLink">
<router-link class="dropdown-item" to="/loginForm">Sign In</router-link>
<router-link class="dropdown-item" to="/registerForm">Sign Up</router-link>
</div>
</li>
</template>
...
</template>
<script>
import configs from '#/configs'
export default{
...
methods{
async logout(){
let logoutURL = configs.serverURL + configs.serverPort + '/logout';
await this.$store.dispatch('logout', {logoutURL});
alert(this.$store.getters.currentMsg);
this.$router.push('/');
},
},
...
}
</script>
I figured out a simple way to work around:
use v-show rather than v-if to render bootstrap dropdown.
<li v-show="!isLogin" class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownUserMenuLink"
role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Login/Register</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownUserMenuLink">
<router-link class="dropdown-item" to="/loginForm">Sign In</router-link>
<router-link class="dropdown-item" to="/registerForm">Sign Up</router-link>
</div>
</li>
According to guidline of Vue2:
v-if is “real” conditional rendering because it ensures that event listeners and child components inside the conditional block are properly destroyed and re-created during toggles. In comparison, v-show is much simpler - the element is always rendered regardless of initial condition, with CSS-based toggling.
I think sth wrong while re-creating "dropdown". Maybe someone can help me to figure out how to use "v-if".

Submenu didn't work and became a text only

I have a problem with submenu that I use from bootstrap. It became a text not a button to go another page, here is some code that I use
This is the latest vue and bootstrap 4, I've tried some tutorial's but didn't work
<div id="sidebar-menu" class="main_menu_side hidden-print main_menu">
<div class="menu_section">
<ul class="nav side-menu">
<li><a><i class="fa fa-edit"></i> Penjualan <span class="fa fa-chevron-down"></span></a>
<ul class="nav child_menu">
<li><a>Pemesanan penjualan<span class="fa fa-chevron-down"></span></a>
<ul class="nav child_menu">
<li><router-link to="/pemesananPembelian">P.O Penjualan</router-link></li>
<li><router-link to="/konfirmasipemesananPenjualan">P.O Konfirmasi</router-link></li>
<li><router-link to="/dikirimpemesananPenjualan">P.O Dikirim</router-link></li>
<li><router-link to="/batalpemesananPenjualan">P.O Batal</router-link></li>
</ul>
</li>
I want to make a sub menu to go another page, but the submenu didn't work and become a textenter code here
Check out the official doc:
https://router.vuejs.org/api/#router-link
<router-link tag="li" to="/foo">
<a>/foo</a>
</router-link>

VueJS exact-active-class

I have a menu using Router-link, and i want to put class "Active" on "li" when the Router-link was actived.
<ul class="nav nav-second-level">
<li v-for="item in menu">
<router-link :to="{ name: somewhere }" tag="a" exact-active-class="IS-ACTIVATED">
{{Name}}
</router-link>
</li>
</ul>
Is there a way to set a class to parent using "exact-active-class"?
Thanks!
You can use v-if to check which route you're in and add class if $route.name is somewhere
<ul class="nav nav-second-level">
<li v-for="item in menu" :class="{ 'IS-ACTIVATED': $route.name === somewhere }">
<router-link :to="{ name: somewhere }">
{{Name}}
</router-link>
</li>
</ul>
One way to do this might be:
<ul class="nav nav-second-level">
<router-link :to="{ name: somewhere }" tag="li" exact-active-class="IS-ACTIVATED">
{{Name}}
</router-link>
</ul>
Otherwise, no, you can't set it on a parent. I would recommend adapting your CSS instead. Also, you don't need to specify tag on router-link, it defaults to "a".