I have a bottom navbar with icons. How can when the router-link has class 'active' it will turn into an active icon? Default icon 1 is active.
<li>
<router-link v-if="active" :class="{active}" active-class="active">
Icon 1 active
</router-link>
<router-link v-if="!active" :class="{active}" active-class="active">
Icon 1
</router-link>
</li>
<li>
<router-link v-if="active" :class="{active}" active-class="active">
Icon 2 active
</router-link>
<router-link v-if="!active" :class="{active}" active-class="active">
Icon 2
</router-link>
</li>
<li>
<router-link v-if="active" :class="{active}" active-class="active">
Icon 3 active
</router-link>
<router-link v-if="!active" :class="{active}" active-class="active">
Icon 3
</router-link>
</li>
The solution:
Binding the HTML class:
<li>
<router-link #click="isActive1 = true" :class="{ active: isActive1 }">
Icon 1
</router-link>
</li>
<li>
<router-link #click="isActive2 = true" :class="{ active: isActive2 }">
Icon 2
</router-link>
</li>
<li>
<router-link #click="isActive3 = true" :class="{ active: isActive3 }">
Icon 3 Active
</router-link>
</li>
Implement isActive variables:
data: {
isActive1: true, // default icon1
isActive2: false,
isActive3: false,
}
And implement watchers:
watch: {
isActive1(val) {
if(val === true) {
this.isActive2 = false
this.isActive3 = false
}
},
isActive2(val) {
if(val === true) {
this.isActive1 = false
this.isActive3 = false
}
},
isActive3(val) {
if(val === true) {
this.isActive1 = false
this.isActive2 = false
}
}
}
Documentation: https://v2.vuejs.org/v2/guide/class-and-style.html
Related
I have link something like this:
<li class="list-item active"> <router-link :to="{name:'link-1'}"> Link 1</router-link>
<li class="list-item"> <router-link :to="{name:'link-2'}"> Link 2</router-link>
I want to add class active with list-item class based on the url suppose if the url is localhost:8000/link-1 then link-1 should be active.
If you are using Vue Router > 3.1.0 you can use <router-link> and a scoped slot for this:
<router-link
:to="{name:'link-1'}"
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<li
:class="[isActive && 'active', isExactActive && 'exact-active']"
>
<a :href="href" #click="navigate">Link 1</a>
</li>
</router-link>
<router-link
:to="{name:'link-2'}"
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<li
:class="[isActive && 'active', isExactActive && 'exact-active']"
>
<a :href="href" #click="navigate">Link 2</a>
</li>
</router-link>
If you don't need to have .active on the li elements, <router-link> has this build in. (See Dan's awnswer)
Vue router provides this functionality. When a link is active, it will have the router-link-exact-active class applied.
In your CSS:
.router-link-exact-active {
// styles here
}
There is also router-link-active which would match both "/" and "/link-1".
When I click sub menu in the following template, how do I add an active class to the <a> of a sibling <router-link> (of menu1 or menu2)?
<ul class="depth1">
<li class="m1">
<router-link to="/introduce/Introduce" #click="selected = 1" :class="{ active: selected == 1 }"><span>menu1</span></router-link>
<ul class="depth2 sm1">
<li><router-link to="" #click="selected = 1">sub menu</router-link></li>
<li><router-link to="" #click="selected = 1">sub menu</router-link></li>
<li><router-link to="" #click="selected = 1">sub menu</router-link></li>
</ul>
</li>
<li class="m2">
<router-link to="/introduce/Introduce" #click="selected = 2" :class="{ active: selected == 2 }"><span>menu2</span></router-link>
<ul class="depth2 sm1">
<li><router-link to="" #click="selected = 2">sub menu</router-link></li>
<li><router-link to="" #click="selected = 2">sub menu</router-link></li>
<li><router-link to="" #click="selected = 2">sub menu</router-link></li>
</ul>
</li>
</ul>
<script>
export default {
data: function () {
return {
selected: false,
};
},
methods: {
},
};
</script>
Your attempt is close to working, but the click handler isn't called because it's not applied correctly.
To add a click handler to the root element of <router-link> (i.e., the <a> tag), use #click.native:
<router-link to="" #click.native="selected = 1">sub menu</router-link>
^^^^^^^
but, why not use vue-router's default behaviors (link automatically get active class when the target route is active) like the below demo shows
const Home = {
template: '<div><h2>Home</h2></div>'
}
const About = {
template: '<div><h2>About</h2></div>'
}
const Users = {
template: `
<div>
<h2>Users</h2>
<router-view></router-view>
</div>
`
}
const User = {
template: '<div>{{ $route.params.username }}</div>'
}
const router = new VueRouter({
mode: 'history',
routes: [{
path: '/',
component: Home
},
{
path: '/about',
component: About
},
{
path: '/users',
component: Users,
children: [{
path: ':username',
name: 'user',
component: User
}]
}
]
})
new Vue({
router,
}).$mount('#app')
a.router-link-active {
color: #f66;
}
li.router-link-active a {
color: #f66;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Active Links</h1>
<ul>
<li>
<router-link to="/">/</router-link>
</li>
<li>
<router-link to="/" exact>/ (exact match)</router-link>
</li>
<li>
<router-link to="/users">/users</router-link>
</li>
<li>
<router-link to="/users" exact>/users (exact match)</router-link>
</li>
<li>
<router-link to="/users/evan">/users/evan</router-link>
</li>
<li>
<router-link to="/users/evan#foo">/users/evan#foo</router-link>
</li>
<li>
<router-link :to="{ path: '/users/evan', query: { foo: 'bar' }}">
/users/evan?foo=bar
</router-link>
</li>
<li>
<!-- #635 -->
<router-link :to="{ name: 'user', params: { username: 'evan' }, query: { foo: 'bar' }}" exact>
/users/evan?foo=bar (named view + exact match)
</router-link>
</li>
<li>
<router-link :to="{ path: '/users/evan', query: { foo: 'bar', baz: 'qux' }}">
/users/evan?foo=bar&baz=qux
</router-link>
</li>
<li>
<router-link to="/about">/about</router-link>
</li>
<router-link tag="li" to="/about">
<a>/about (active class on outer element)</a>
</router-link>
</ul>
<router-view class="view"></router-view>
</div>
I am new to Vue and straggling with generating sidebar list where each list element on click open its components. I am using vue router. I understand the theory how it should work, but obviously I am missing something because I can't solve it. I don't know how to dynamically change the path.
I generated sidebar list using router-link
<template>
<div class="sidebarListItems">
<router-link href="#"
:to="calcRut"
class="list-group-item list-group-item-action bg-light"
v-for="title in mapTitles"
:key="title.map"
:title="title.map"
#click="callMap">
{{title.map}}
</router-link>
</div>
</template>
<script>
import bus from "./js/bus"
import mapNames from "./json/popis_karata.json"
export default {
name: "PageSidebarList",
props: ["title"],
data(){
return {
mapTitles:mapNames,
ruth=""
}
},
methods: {
callMap(event){
bus.$emit("openMap")
},
calcRuth(){
for (var i=0; i<this.routes.length; i++){
var newruth = this.routes[i].path
this.ruth = newruth
}
}
},
computed: {
routes(){
return this.$router.options.routes
}
}
When I put my path directly as a string (:to="/zup" or :to="/reg") it's working, but I would like that those paths are dynamically generated depending on which list element I clicked.
Here's how I do it. Try extracting the v-for on the level above. If you don't want to use an actual element, try <template>
<ul class="flex flex-col w-full space-y-1">
<li v-for="item in items" :key="item.link">
<router-link class="flex items-center h-8 px-4 rounded-lg hover:bg-white" :class="{ 'bg-white': routeMatches(item) }" :to="{ name: item.link }">
<div class="text-sm text-gray-700">{{ item.name }}</div>
</router-link>
</li>
</ul>
Edit, also format your to="" correctly to be :to="{name: 'namehere'}"
how about this solution with a switch and a programmatic router change
<template>
<div id="app">
<ul class="nav">
<li class="nav-item" #click="routeChanger('home')">Home</li>
<li class="nav-item" #click="routeChanger('page1')">Page1</li>
<li class="nav-item" #click="routeChanger('page2')">Page2</li>
<li class="nav-item" #click="routeChanger('page3')">Page3</li>
<li class="nav-item" #click="routeChanger('page4')">Page4</li>
</ul>
</div>
</template>
<script>
export default {
name: "App",
methods: {
routeChanger(routeParam) {
switch (routeParam) {
case "home":
this.$router.push("/home");
break;
case "page1":
this.$router.push("/page1");
break;
//...
default:
this.$router.push("/404");
break;
}
}
}
};
</script>
<style>
</style>
Maybe my answer would be useful to someone so I am posting my solution.
I didn't found solution to loop separately navbar elements from one data file and router-lik :to path attribute from routes file.
It work if I use routes file for everything.
<router-link
v-for="route in $router.options.routes"
:key="route.path"
:to="route.path"
class="list-group-item list-group-item-action bg-light">
{{route.title}}
</router-link>
i want to toggle highlighting for the routerLinkActive based on the routes,
i have a dashboard component and both the dashboard menu item and labels menu item refer to the same component, i want to add class to the li items based on the route
for dashboard the route will be
http://localhost:4300/dashboard
and for the labels , the route will be
http://localhost:4300/dashboard/5d1bb53877ed8702d8a01940
Code for the menu item
<ul class="sidebar-nav">
<li [ngClass]="{ active: rlal && rlal.isActive == false }">
<a [routerLink]="['/dashboard']" (click)="loadSnippet(null)">
<mat-icon>dashboard</mat-icon>
<span>Dashboard</span>
</a>
</li>
<h3 *ngIf="labelList && labelList.length!=0">Labels</h3>
<ul class="sidebar-nav">
<li *ngFor="let label of labelList" [ngClass]="rlal && rlal.isActive ? 'active' : ''"
routerLinkActive="rlal.isActive">
<a [routerLink]="['/dashboard', label._id]" routerLinkActive #rlal="routerLinkActive"
(click)="loadSnippet(label)">
<mat-icon>label</mat-icon>
<span>{{ label.label_name }} </span>
</a>
</li>
</ul>
Routes:
{
path: "dashboard",
component: DashboardComponent,
canActivate: [LoggedInGuard]
},
{
path: "dashboard/:labelId",
component: DashboardComponent,
canActivate: [LoggedInGuard]
}
The highlighting works fine for the individual label items, but for the dashboard , the li is not getting highlighted
If you want the dashboard link to be active only if there's no route parameter, you need to add routerLinkActive and [routerLinkActiveOptions]="{exact: true}" to you dashboard li node.
Also, you don't need to set the active class via ngClass, because routerLinkActive will do that for you.
<li routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a routerLink="/dashboard" (click)="loadSnippet(null)">
<mat-icon>dashboard</mat-icon>
<span>Dashboard</span>
</a>
</li>
...
<li routerLinkActive="active" *ngFor="let label of labelList">
<a [routerLink]="['/dashboard', label._id]" (click)="loadSnippet(label)">
<mat-icon>label</mat-icon>
<span>{{ label.label_name }}</span>
</a>
</li>
I did it using jquery inside vue method. But I still want a vue js solution.
I want to remove the class from the li and then add the same class on the clicked li. So here is what I did using jquery plus vue
<ul class="manage-link">
<li class="current 1" #click="run(1,'In progress')">Awaiting approval</li>
<li class="2" #click="run(2,'In progress')">In progress</li>
<li class="3" #click="run(3,'Completed')">Completed</li>
<li class="4" #click="run(4,'Shipped')">Shipped</li>
<li class="5" #click="run(5,'Delivered')">Delivered</li>
<li class="6" #click="run(6,'Cancelled')">Cancelled</li>
<li class="7" #click="run(7,'Under review')">Under review</li>
<li class="pointer"></li>
</ul>
methods: {
run(num, str){
console.log('ok');
$(".manage-link>li").removeClass("current");
$('.'+num).addClass('current');
}
}
Here str is for another task. Not related to this problem. This is working just fine but I want to achieve same thing using only vue js. Thank you.
Super easy using class bindings
<li class="1" :class="{current: current == 1}" #click="run(1)">Awaiting approval</li>
<li class="2" :class="{current: current == 2}" #click="run(2)">In Progress</li>
<li class="3" :class="{current: current == 3}" #click="run(3)">Completed</li>
<!-- and so on -->
data () { // or just "data:" if this is not a component
return {
current: 1
}
},
methods: {
run(num) {
this.current = num
}
}
You can pass $event object in your click handler and then handle the class manipulation there like this:
<ul class="manage-link">
<li class="current 1" #click="run($event, 1,'In progress')">Awaiting approval</li>
<li class="2" #click="run($event, 2,'In progress')">In progress</li>
<li class="3" #click="run($event, 3,'Completed')">Completed</li>
<li class="4" #click="run($event, 4,'Shipped')">Shipped</li>
<li class="5" #click="run($event, 5,'Delivered')">Delivered</li>
<li class="6" #click="run($event, 6,'Cancelled')">Cancelled</li>
<li class="7" #click="run($event, 7,'Under review')">Under review</li>
<li class="pointer"></li>
</ul>
methods: {
run(ev, num, str){
console.log('ok');
[].slice.call(ev.target.parentNode.children).forEach(function(child) {
child.classList.remove('current');
});
ev.target.classList.add('current');
}
}
you can use $index to save active element.
such as
<ul>
<li
<!-- begin snippet: js hide: false console: true babel: false -->
<ul class="manage-link">
<li class="current 1" :class="{ active ? activeIndex === 1 }"#click="run(1,'In progress')">Awaiting approval</li>
<li class="2" :class="{ active ? activeIndex === 2 } #click="run(2,'In progress')">In progress</li>
<li class="3" :class="{ active ? activeIndex === 3 } #click="run(3,'Completed')">Completed</li>
<li class="4" :class="{ active ? activeIndex === 4 } #click="run(4,'Shipped')">Shipped</li>
<li class="5" :class="{ active ? activeIndex === 5 } #click="run(5,'Delivered')">Delivered</li>
<li class="6" :class="{ active ? activeIndex === 6 } #click="run(6,'Cancelled')">Cancelled</li>
<li class="7" :class="{ active ? activeIndex === 7 } #click="run(7,'Under review')">Under review</li>
<li class="pointer"></li>
</ul>
data: {
return {
activeIndex: -1,
};
},
methods: {
run(num, str){
this.activeIndex = num;
}
}
</ul>