Vue router-link active-class when some child route duplicates the parent - vue.js

I have a routing structure such as:
{
path: '/post/'
component: Post,
children: [
{
path: '/',
component: Text,
},
{
path: 'text/',
component: Text,
},
{
path: 'video/',
component: Video,
},
],
}
The important thing is that the route /post/text/ is just an "alias" to the /post/ (root point in this case). Also the router configuration has the custom linkActiveClass option such as 'act' with some defined styles.
In the parent template I have the kind of tabs with router-link's:
<ul>
<li>
<router-link to="/post/text/">Text</router-link>
</li>
<li>
<router-link to="/post/video/">Video</router-link>
</li>
</ul>
So the question is: when going to the /post/ route it would be nice the Text tab to be marked as "active" with that 'act' class because it's just a duplicate of the /post/text/ (or vice versa).
I haven't found any mention of this in the Vue Router docs. How such problems are solved competently?
Thanks in advance!
Solution (TL;DR - currently unsupported, fixed in v4)
The easiest way to achieve this - to use route alias prop, like:
{
path: 'text/',
component: Text,
alias: '/',
},
But, unfortunately, active-class doesn't work properly with aliases yet. It has been fixed in Vue Router v4.

You can do something like this
<ul>
<li :class="[currentPage.includes('/post/text/') ? 'activeClass' : 'no-active-class']">
<router-link to="/post/text/">Text</router-link>
</li>
<li :class="[currentPage.includes('/post/video/') ? 'activeClass' : 'no-active-class']">
<router-link to="/post/video/">Video</router-link>
</li>
</ul>
If your header or browser is a single component, you can include all this functionality in each of your links and it should work correctly.
NOTE: The variable with your current path will depend on the type of project and certain settings in it.

Use the Vuex state.
When every pages go to the tab area are mounted, call a setter for currentPage state.
And you can give an active class according to the state value
<li>
<div :class="{ 'active': currentPage === 'Text'}">
<router-link to="text">Text</router-link>></div>
</li>
<li>
<div :class="{ 'active': currentPage === 'Video'}">
<router-link to="video">Video</router-link>></div>
</li>

using exact-active-class solved a similar problem to yours.
docs: https://router.vuejs.org/api/#exact-active-class

Related

Passing prop value after switching tabs

I have one header page that has two tabs. The two tabs direct to two different pages. I am using the area prop that I defined in the header page router view for the system page. It works fine when I am on the system page, but when I go to the product tab and come back to system tab, the prop in the system tab
loses the value. It shows as the string "area" in the prop. I would like to get the same value when I switch tabs.
<div class="tabs">
<ul>
<li #click="$router.push({ name: 'system' })">
<a>SYSTEM</a>
</li>
<li #click="$router.push({ name: 'product' })">
<a>Product</a>
</li>
</ul>
</div>
<router-view :area="area"/>
</div>
</template>
System view:
export default {
props: ['area'],
Instead of using #click binding you will be much better off using <router-link> especially since you are using the router-view
this will allow you to pass params simply in your app.
https://router.vuejs.org/api/
you declare your routes like so
const routes = [
{
path: '/glossary/:subjectName',
name: 'glossaryWithParams',
component: GlossaryPage,
},
]
Then in your code you can build the element simply like so:
<router-link
:to="`/glossary/${subjectName}`"
class="tabs__link"
>
Subject Glossary
</router-link>
```
Once you implement Router-link your history push state will also be resolved, and your params will work normally.

vue.js "TypeError: Cannot read property 'path' of undefined"

I know this is a common question, but I have been going through my files now so many times without being able to the locate the error.
I am getting this error when I try to route to my components in my navigation menu.
My app.vue file:
<template>
<div id="app">
<Navbar
:nav-links="navLinks"
/>
<router-view/>
</div>
</template>
<script>
import Navbar from '#/components/Navbar'
export default {
components: {
Navbar
},
data: () => ({
navLinks: [
{
text: 'Home',
path: '/home'
},
{
text: 'About',
path: '/about'
},
{
text: 'Contact',
path: '/contact'
}
]
})
}
</script>
My Navbar component (This is where the error happens)
<template>
<nav>
<ul>
<li v-for="{link, index} in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false">
<router-link :to="link.path">
{{ link.text }}
</router-link>
</li>
</ul>
</nav>
</template>
<script>
export default {
props: ['navLinks'],
data(){
return {
hover: false,
}
}
}
</script>
How do I fix this?
<li v-for="{link, index} in navLinks" :key="index"...
should be
<li v-for="(link, index) in navLinks" :key="index"...
As it's now (destructured), link refers to a link property inside the object, not the object itself. Additionally, index is probably undefined, since the navLinks objects probably don't have an explicit property index. Therefore Vue might also complain about using invalid indexes in v-for.
Since you're only using the path prop, you could actually use destructuring, like this:
<li v-for="({ path }, index) in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false">
<router-link :to="path">
</li>
Another, unrelated note: hover property is currently being shared across all navLinks. If you expect it to somehow be related to the currently hovered element, yo uhave to save that separately (probably inside the navLink itself).
As for :nav-links="navLinks", what you've done is not only perfectly legal, but the recommended way of doing it (it's according to the HTML spec). Using :navLinks="navLinks" relies on Vue's HTML parser, which converts it to nav-links behind the scenes - inspect the HTML element and you'll notice it).
If you want to get into the details, you could have a look at this discussion on the subject. The result was: use either, but if you use camelCase it will be inconsistent with the rendered markup. If you use kebab-case, it will be consistent with rendered markup, so you won't have to deal with this difference when writing tests, for example, should you ever need to select elements by their attributes (jest converts camelCase to lowercase - hence it's inconsistent with the rendered markup, so the tests start passing/failing based on whether mount or shallowMount is used. Goes without saying, that's not really a good testing setup. )
The same exact discussion goes for using <SomeComponent /> vs <some-component />. While both work, using first needs to be addressed when writing tests if you need to select stubbed subcomponents.
Besides, vue/attribute-hyphenation (the way you did it) is part of the following vue linting presets:
plugin:vue/strongly-recommended
plugin:vue/vue3-recommended
plugin:vue/recommended
A prop in the Navbar component is named navLinks but you access it outside as nav-links.
This should work:
:navLinks="navLinks"
Incorrect syntax for v-for with {}. Use ():
li v-for="(link, index) in navLinks
You have done two mistakes here.
one is:
<template>
<div id="app">
<Navbar
:nav-links="navLinks"
/>
<router-view/>
</div>
Here you are binding with different name(nav-links), you should keep same name with which you are binding data and the name inside the props(navLinks).
Both names should be same.
Second one:
v-for="{link, index} in navLinks"
The syntax is wrong, the correct syntax should be
v-for="(link, index) in navLinks"

To reopen the same page with different filter

In my Laravel 5/vuejs 2/ VueRouter / app I have navigation area :
<li class="navigation_item_top">
<router-link :to="{ name: 'DashboardIndex' }" class="a_link">
Dashboard
</router-link>
</li>
<li class="active navigation_item_top" >
Customers
<ul class="collapse list-unstyled ml-2" id="backendCustomersSubmenu">
<li class="mt-2">
<router-link :to="{name : 'listCustomers'}" class="a_link">
Customers
</router-link>
</li>
<li class="mt-2">
<router-link :to="{name : 'listCustomers', params: { filter: 'new' } }" class="a_link">
New Customers
</router-link>
</li>
<li class="mt-2">
<router-link :to="{name : 'listCustomers', params: { filter: 'inactive' } }" class="a_link">
Inactive Customers
</router-link>
</li>
</ul>
</li>
Where there are 3 links listCustomers with different default filter opening page and it works ok if from
DashboardIndex page I move to any listCustomers.
But it does not work if from opened listCustomers page I open listCustomers with other filter.
I suppose as VueRouter considers all listCustomers as 1 page.
If there is a way to make it and selecting other listCustomers to reopen filter?
Not sure if i understand your Q very well, but i think you want to:
Append query to url. e.g company.com/listCustomers?filter=new
Make Vue notice the change and do something with that change
If that's the case then try this :
<template>
<--! HTML HERE -->
</template>
<script>
export default {
watch: {
'$route'(to, from){
// you don't need this but just in-case.
//this.$forceUpdate();
//If you're using query like ?filter=new, this should work
if (this.$route.query.filter) {
if (this.$route.query.filter== 'new') {
}else{
}
}
}
}
}
</script>
N.B
If you want use parameters it means you expect your url to be:
domain.com/listCustomers/inactive.
so just try the basics and link like this:
<router-link to="/listCustomers/inactive">
or
<router-link to="/listCustomers/new">.
And if your want queries your url is going to be:
domain.com/listCustomers?filter=new.
and you should pass exact prop to activate active page style.
then you need to watch for changes in the watch hook just like i did my answer
Now all the been said,
linking with parameters should work without any problem, but if you decide to use any Navigation Guards technique, like router.afterEach.
Please do not forget to add next(), to allow it to move on after your code. otherwise it won't navigate.
read Navigation Guards.
I hope you will understand.

How to VueJS router-link active style

My page currently has Navigation.vue component.
I want to make the each navigation hover and active. The hover works but active doesn't.
This is how Navigation.vue file looks like :
<template>
<div>
<nav class="navbar navbar-expand-lg fixed-top row">
<router-link tag="li" class="col" class-active="active" to="/" exact>TIME</router-link>
<router-link tag="li" class="col" class-active="active" to="/CNN" exact>CNN</router-link>
<router-link tag="li" class="col" class-active="active" to="/TechCrunch" exact>TechCrunch</router-link>
<router-link tag="li" class="col" class-active="active" to="/BBCSport" exact>BBC Sport</router-link>
</nav>
</div>
</template>
And the following is the style.
<style>
nav li:hover,
nav li:active {
background-color: indianred;
cursor: pointer;
}
</style>
This is how hover looks like now and expected exactly same on active.
I would appreciate if you give me an advice for styling router-link active works.
Thanks.
The :active pseudo-class is not the same as adding a class to style the element.
The :active CSS pseudo-class represents an element (such as a button)
that is being activated by the user. When using a mouse, "activation"
typically starts when the mouse button is pressed down and ends when
it is released.
What we are looking for is a class, such as .active, which we can use to style the navigation item.
For a clearer example of the difference between :active and .active see the following snippet:
li:active {
background-color: #35495E;
}
li.active {
background-color: #41B883;
}
<ul>
<li>:active (pseudo-class) - Click me!</li>
<li class="active">.active (class)</li>
</ul>
Vue-Router
vue-router automatically applies two active classes, .router-link-active and .router-link-exact-active, to the <router-link> component.
router-link-active
This class is applied automatically to the <router-link> component when its target route is matched.
The way this works is by using an inclusive match behavior. For example, <router-link to="/foo"> will get this class applied as long as the current path starts with /foo/ or is /foo.
So, if we had <router-link to="/foo"> and <router-link to="/foo/bar">, both components would get the router-link-active class when the path is /foo/bar.
router-link-exact-active
This class is applied automatically to the <router-link> component when its target route is an exact match. Take into consideration that both classes, router-link-active and router-link-exact-active, will be applied to the component in this case.
Using the same example, if we had <router-link to="/foo"> and <router-link to="/foo/bar">, the router-link-exact-activeclass would only be applied to <router-link to="/foo/bar"> when the path is /foo/bar.
The exact prop
Lets say we have <router-link to="/">, what will happen is that this component will be active for every route. This may not be something that we want, so we can use the exact prop like so: <router-link to="/" exact>. Now the component will only get the active class applied when it is an exact match at /.
CSS
We can use these classes to style our element, like so:
nav li:hover,
nav li.router-link-active,
nav li.router-link-exact-active {
background-color: indianred;
cursor: pointer;
}
The <router-link> tag was changed using the tag prop, <router-link tag="li" />.
Change default classes globally
If we wish to change the default classes provided by vue-router globally, we can do so by passing some options to the vue-router instance like so:
const router = new VueRouter({
routes,
linkActiveClass: "active",
linkExactActiveClass: "exact-active",
})
Change default classes per component instance (<router-link>)
If instead we want to change the default classes per <router-link> and not globally, we can do so by using the active-class and exact-active-class attributes like so:
<router-link to="/foo" active-class="active">foo</router-link>
<router-link to="/bar" exact-active-class="exact-active">bar</router-link>
v-slot API
Vue Router 3.1.0+ offers low level customization through a scoped slot. This comes handy when we wish to style the wrapper element, like a list element <li>, but still keep the navigation logic in the anchor element <a>.
<router-link
to="/foo"
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<li
:class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']"
>
<a :href="href" #click="navigate">{{ route.fullPath }}</a>
</li>
</router-link>
https://router.vuejs.org/en/api/router-link.html
add attribute active-class="active"
eg:
<ul class="nav navbar-nav">
<router-link tag="li" active-class="active" to="/" exact><a>Home</a></router-link>
<router-link tag="li" active-class="active" to="/about"><a>About</a></router-link>
<router-link tag="li" active-class="active" to="/permission-list"><a>Permisison</a></router-link>
</ul>
When you are creating the router, you can specify the linkExactActiveClass as a property to set the class that will be used for the active router link.
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes,
linkActiveClass: "active", // active class for non-exact links.
linkExactActiveClass: "active" // active class for *exact* links.
})
This is documented here.
As mentioned above by #Ricky vue-router automatically applies two active classes, .router-link-active and .router-link-exact-active, to the <router-link> component.
So, to change active link css use:
.router-link-exact-active {
//your desired design when link is clicked
font-weight: 700;
}
Just add to #Bert's solution to make it more clear:
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes,
linkExactActiveClass: "active" // active class for *exact* links.
})
As one can see, this line should be removed:
linkActiveClass: "active", // active class for non-exact links.
this way, ONLY the current link is hi-lighted. This should apply to most of the cases.
David
Let's make things simple, you don't need to read the document about a "custom tag" (as a 16 years web developer, I have enough this kind of tags, such as in struts, webwork, jsp, rails and now it's vuejs)
just press F12, and you will see the source code like:
<div>
page1
page2
page3
</div>
so just add styles for the .router-link-active or .router-link-exact-active
If you want more details, check the router-link api:
https://router.vuejs.org/guide/#router-link

Nav not showing up in aurelia

I am learning Aurelia and have stuck at this for quite a while. I want to add a navigation bar at the top of the screen using routing in Aurelia.
app.js:
export class App {
configureRouter(config, router) {
this.router = router;
config.map([
{
route: "add",
moduleId: "./add",
title: "Add new Post",
nav: "true",
name: "Add"
}
]);
}
... rest of the code
app.html:
<template>
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav">
<li repeat.for="row of router.navigation">
<a href.bind="row.href">${row.title}</a>
</li>
</ul>
</div>
</nav>
</template>
Nothing shows up when I load the app. The navbar remains empty. Is there any required dependency to use the router? Am I missing anything? Thanks.
Note: Not sure if this is enough context to answer my question. I can add config.js or any other file if it's required.
Update : I replaced ${row.title} with hello (any hard coded string) and it's not showing up.
Thanks to comment from doeck I fixed the issue. I had to do two things:
add a <router-view></router-view> tag in my app.html
add an empty route in my app.js as follows: route: ["", "home"],
(When I didn't give the empty route, aurelia gave an error asking route "/" is not defined.)