vue-router icon indicating copy to clipboard operation
vue-router copied to clipboard

Allow defining an inactive-class

Open christhofer opened this issue 5 years ago • 39 comments

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?

<router-link
    :to="{name: 'purchase'}"
    class="btn"
    active-class="btn-primary"
    inactive-class="btn-secondary">
    Purchase
</router-link>

christhofer avatar Mar 14 '19 02:03 christhofer

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>

martiendt avatar Mar 14 '19 07:03 martiendt

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

christhofer avatar Mar 14 '19 07:03 christhofer

Add a regular class to those specific links and override them with the active class: class="btn btn-secondary" active-class="btn-primary"

posva avatar Mar 15 '19 07:03 posva

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"

christhofer avatar Mar 15 '19 12:03 christhofer

I'm reopening this as it does make sense for utility-first CSS frameworks like Tailwind

posva avatar May 02 '19 14:05 posva

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

posva avatar Apr 30 '20 10:04 posva

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.

christhofer avatar Apr 30 '20 11:04 christhofer

It could also be isExactActive ? exactActiveClass : exactInactiveClass

posva avatar Apr 30 '20 11:04 posva

At this point why not add both inactive-class and exact-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?

mdwheele avatar May 02 '20 03:05 mdwheele

Thank you but let's wait for more feedback

posva avatar May 02 '20 07:05 posva

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 😂

mdwheele avatar Jun 17 '20 19:06 mdwheele

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

posva avatar Jun 17 '20 20:06 posva

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 the v-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.

mdwheele avatar Jun 17 '20 21:06 mdwheele

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 avatar Jun 17 '20 21:06 posva

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;

ImJustAMan avatar Jul 10 '20 10:07 ImJustAMan

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

posva avatar Aug 03 '20 09:08 posva

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.

christhofer avatar Aug 03 '20 12:08 christhofer

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.

cosmini avatar Sep 01 '20 06:09 cosmini

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

ImJustAMan avatar Sep 01 '20 06:09 ImJustAMan

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?

christhofer avatar Sep 10 '20 06:09 christhofer

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

posva avatar Sep 10 '20 07:09 posva

since this solution works on Vue router for Vue 2

Got it. Will try this in my next project.

christhofer avatar Sep 10 '20 08:09 christhofer

@posva , I see that $listeners is deprecated in vue 3. Any alternative for this? I can't find about it in v3 docs.

christhofer avatar Sep 17 '20 03:09 christhofer

@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

durub avatar Dec 15 '20 13:12 durub

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.

vlasscontreras avatar Aug 09 '21 17:08 vlasscontreras

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.

ddahan avatar Dec 28 '21 17:12 ddahan

I encapsulated a component myself to implement the function of vue-router 3, WTF

apprat avatar Feb 03 '22 08:02 apprat

+1 to adding this feature.

kilakewe avatar Feb 05 '22 03:02 kilakewe

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.

hyrumwhite avatar Dec 19 '22 14:12 hyrumwhite

Any update?

mukundshah avatar Mar 08 '23 02:03 mukundshah