Open christhofer opened 5 years ago
You can use class binding and compare it with your routes
<router-link
to="/purchase/purchase-request"
class="btn"
:class="{'btn-secondary': this.$route.path != '/purchase/purchase-request'}"
active-class="btn-primary">
<span><i class="si si-folder-alt"></i> Purchase Request</span>
</router-link>
You can use class binding and compare it with your routes
<router-link to="/purchase/purchase-request" class="btn" :class="{'btn-secondary': this.$route.path != '/purchase/purchase-request'}" active-class="btn-primary"> <span><i class="si si-folder-alt"></i> Purchase Request</span> </router-link>
Still need additional logic if want to match purchase/purchase-request/create
and other link
You need indexOf !== -1
for this
Add a regular class to those specific links and override them with the active class: class="btn btn-secondary" active-class="btn-primary"
Add a regular class to those specific links and override them with the active class:
class="btn btn-secondary" active-class="btn-primary"
@posva but that will be rendered as class="btn btn-secondary btn-primary"
instead of class="btn btn-primary"
I'm reopening this as it does make sense for utility-first CSS frameworks like Tailwind
I realized the feature request isn't clear, especially given the changes on v4 for the active/exact active behavior: https://github.com/vuejs/rfcs/pull/136
Should inactive be non active or non exact active? At this point why not add both inactive-class
and exact-inactive-class
? Which would create 4 classes that can be overridden by the user, making it more complicated for beginners.
It's important to note that in v4, exact-active is likely to be the class most people will hook on because links are active if any of their children are active (see RFC). TLDR: Exact active will be active only if the link corresponds to the actual location the user is at, there is no need for exact
prop anymore
I don't quite understand the confusion about this request. High prio high complex?
Vue-router already know when to append active class Can't we just make it a simple ternary
isActive ? activeClass : inactiveClass
With inactiveClass default value is empty string. So if user doesn't set inactive class, it will just append empty string.
It could also be isExactActive ? exactActiveClass : exactInactiveClass
At this point why not add both
inactive-class
andexact-inactive-class
?
Yeah, I could totally see that being useful. Would you like me to go ahead and put that into #3184 with appropriate tests?
Thank you but let's wait for more feedback
Hey @posva!
Do you have an update on whether or not this will be supported in vue-router
? It's one of those things that's only a thorn in the side when I happen to touch our lower-level navigation components, so no big deal. But today was one of those days for me 😂
There is no update on this. Keep in mind it's completely fine to extend RouterLink
and add your own props while using the v-slot
api to implement this feature request
There is no update on this.
No worries! Just curious.
Keep in mind it's completely fine to extend
RouterLink
and add your own props while using thev-slot
api to implement this feature request
That's exactly what we do, but it is not legible code to immediately see what's happening and it breaks a lot of the productivity benefits we otherwise get from Tailwind CSS (to have to switch gears mentally). I'd prefer to not have to abstract even further to hide some of these details, but it's likely coming to that soon (read: if I have to touch this code again, it's happening).
I do think having a way to apply a CSS class when the <router-link>
are inactive would be very useful to users of Tailwind, Tachyons, Basscss, etc. so I'm hopeful we can implement at some point. Otherwise, I dread having to NavLink
all over the place, but it is what it is.
I mean creating a custom component that extends router link so you can write in your code <my-router-link inactive-class="something"/>
. You write it once, use it everywhere:
<template>
<router-link v-bind="$props" v-slot="{ isActive, href, navigate }">
<a
v-bind="$attrs"
v-on="$listeners"
:href="href"
@click="navigate"
:class="isActive ? activeClass : inactiveClass"
>
<slot />
</a>
</router-link>
</template>
<script lang="ts">
import Vue from 'vue'
const RouterLink = Vue.component('RouterLink')
export default {
name: 'AppLink',
props: {
// @ts-ignore
...RouterLink.options.props,
inactiveClass: String,
},
}
</script>
I really thought that if expose isActive
and isExactActive
, we don't need inactive-class any more. we can just use <router-link :class="isActive ? activeClass : inactiveClass"/>
.I think thats enough.Maybe we don't even need active-class anymore;
Given the current feedback I think it is more important to document how to extend RouterLink via the v-slot
api to create custom router links (example at https://github.com/vuejs/vue-router/issues/2648#issuecomment-645643957) that fit the need of any application than adding two new props + two new global options (for consistency).
While I agree this is essential when using Tailwind CSS (being a big fan myself), it's still something that most of users are not using and ends up being useless for anybody not using utility-first CSS libraries. On top of that, even it those scenarios, you still have to decide between exact inactive and inactive class
If we can get the active / exactActive value from vue router, I'm fine with custom component that wraps router link component. I wrapped all of my components that comes from a library, and defining most of the props there, so that I don't writes the same props every time I use the component.
In my opinion if you provide option for custom class for some state, it should cover all possible states. Having only active state is not good enough. Complex applications are detailed and we use vue, vue-router frameworks so we can created such apps easier.
In my opinion if you provide option for custom class for some state, it should cover all possible states. Having only active state is not good enough. Complex applications are detailed and we use vue, vue-router frameworks so we can created such apps easier.
agree with you. If we can get all options(like active and exactActive).I'm sure about we can use those state that make our apps easier
Now that Laravel 8 uses Tailwind as the default, this use case should becomes more common, isn't it?
I mean creating a custom component that extends router link so you can write in your code
<my-router-link inactive-class="something"/>
. You write it once, use it everywhere:<template> <router-link v-bind="$props" v-slot="{ isActive, href, navigate }"> <a v-bind="$attrs" v-on="$listeners" :href="href" @click="navigate" :class="isActive ? activeClass : inactiveClass" > <slot /> </a> </router-link> </template> <script lang="ts"> import Vue from 'vue' const RouterLink = Vue.component('RouterLink') export default { name: 'AppLink', props: { // @ts-ignore ...RouterLink.options.props, inactiveClass: String, }, } </script>
@posva Is this already possible? or should we wait for Vue 3 to be able to do this?
The thing is with TailwindCSS you end up creating your own custom link anyway because you don't want to write a big list of class every time you write a link:
Custom NavLink.vue
reusing AppLink
from https://github.com/vuejs/vue-router/issues/2648#issuecomment-645643957:
<template>
<AppLink
v-bind="$attrs"
class="inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 focus:outline-none transition duration-150 ease-in-out hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out"
active-class="border-indigo-500 text-gray-900 focus:border-indigo-700"
inactive-class="text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:text-gray-700 focus:border-gray-300"
>
<slot />
</AppLink>
</template>
It will take more time for this to be adopted, so let's just focus on other more important problems for the moment since this solution works on Vue router for Vue 2
since this solution works on Vue router for Vue 2
Got it. Will try this in my next project.
@posva , I see that $listeners is deprecated in vue 3. Any alternative for this? I can't find about it in v3 docs.
@christhofer, $listeners
is now part of $attrs
.
You can read more about it in the migration guide: https://v3.vuejs.org/guide/migration/listeners-removed.html
Anyone interested in how to implement @posva's solution in Vue Router 4 + Vue 3, there's a full example in the extending RouterLink page.
Like some other users, I have a strong feeling that extending a Vue Component by myself for such a basic need is a non sense. Even if it's technically possible, it would makes the code more complex for no good reason.
The fact that the API isn't consistent by default (active-class
without inactive-class
) makes me think of a mistake in the conception, like the developer did not think of a use case in which specifying inactive-class
could be useful for users.
Please consider adding it, especially if it's not a complex feature and wanted by many.
I encapsulated a component myself to implement the function of vue-router 3, WTF
+1 to adding this feature.
Ooh, is this feature coming soon? There's some commented out code for inactive classes in the RouterLink component. Really think it'd be a nice feature.
Any update?
The worst part for me is that active-class address the classes to the beginning of the class statement. This makes it impossible to have the active class overwrite the inactive class. That would be a nice workaround if it worked.
+1 to adding inactive-class
The worst part for me is that active-class address the classes to the beginning of the class statement. This makes it impossible to have the active class overwrite the inactive class. That would be a nice workaround if it worked.
+1 to adding inactive-class
It doesn't work like that, it's the order in the CSS file that matters.
maybe we can use clsx or something like tailwind-merge.
i will also check into it.
Perhaps this will help someone. When using tailwind, you can apply this trick:
<router-link :to="...." active-class="!text-rose-500" class="text-gray-500">
<component :is="icon" />
</router-link>
Just use the !
Another trick is using :not()
pseudo-class. For example (Tailwind):
.nav-link {
&.active {
@apply border-indigo-500 text-gray-900 focus:border-indigo-700
}
&:not(.active) {
@apply text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:text-gray-700 focus:border-gray-300
}
}
<RouterLink
to="/"
active-class="active"
class="nav-link inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out"
>
Home
</RouterLink>
Solved this issue in same way as @uonick did. Using !
+ exact
since using nested routes.
<router-link
to="path"
exact-active-class="!border-indigo-500 !text-gray-900"
class="inline-flex items-center border-b-2 border-indigo-500 px-1 pt-1 text-sm font-medium border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700"
exact
>Navbar Item</router-link>
I'm following the example in the extending RouterLink page but I can't get it to work with typescript
+ composition
API.
Anyone got it working while using both the composition API and typescript?
+1 to adding inactive-class
as it'd be really handy
I'm posting the typescript solution here in case anyone else is having the same trouble as I did to get it working by following the official docs.
Inside AppLink.vue
:
<script lang="ts">
export default {
inheritAttrs: false,
}
</script>
<script setup lang="ts">
import { computed, toRefs } from 'vue'
import { RouterLink } from 'vue-router'
const props = defineProps({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...RouterLink.props,
activeClass: {
type: String,
default: '',
},
inactiveClass: {
type: String,
default: '',
},
})
const { to, activeClass, inactiveClass } = toRefs(props)
const isExternalLink = computed(() => typeof to.value === 'string' && to.value.startsWith('http'))
</script>
<template>
<a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
<slot />
</a>
<RouterLink v-else v-slot="{ isActive, href, navigate }" v-bind="$props" custom>
<a v-bind="$attrs" :href="href" :class="isActive ? activeClass : inactiveClass" @click="navigate"
:aria-current="isActive ? 'page' : false">
<slot />
</a>
</RouterLink>
</template>
Then in your own component file where you need to use the custom RouterLink:
<script setup lang="ts">
import { ref } from 'vue'
import AppLink from './AppLink.vue'
const activeClass = ref("block py-2 pr-4 pl-3 text-white rounded bg-primary-700 lg:bg-transparent lg:text-primary-700 lg:p-0 dark:text-white")
const inactiveClass = ref("block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-primary-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700")
</script>
<template>
<!-- This is just an example-->
<ul class="flex flex-col mt-4 font-medium lg:flex-row lg:space-x-8 lg:mt-0">
<li>
<AppLink :to="{ name: 'home' }" :active-class="activeClass" :inactive-class="inactiveClass">
Home
</AppLink>
</li>
<li>
<AppLink :to="{ name: 'about' }" :active-class="activeClass" :inactive-class="inactiveClass">
About</AppLink>
</li>
</ul>
</template>
I think that the easiest way how to use inactive classes bay be the Link component from Nuxt UI.
The Link component is a wrapper around
<NuxtLink>
through the custom prop that provides a few extra props:
- inactive-class prop to set a class when the link is inactive, active-class is used when active.
What problem does this feature solve?
We already have prop
active-class
that will be appended to the element when the route is active. But what if that we need to specify class only when the route is not active?example: when active =>
.btn-primary
when inactive =>.btn-secondary
What does the proposed API look like?