Skip to content

Cannot prevent router-link event #916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
floorish opened this issue Nov 18, 2016 · 34 comments
Closed

Cannot prevent router-link event #916

floorish opened this issue Nov 18, 2016 · 34 comments

Comments

@floorish
Copy link

It looks like the click event on the <router-link> is handled before any custom click events:

<router-link to="/b" @click.native.prevent="clicked">should NOT route to b</router-link>

const app = new Vue({
  router,
  methods: {
  	clicked: function(e) {
        // Does not prevent route change
    	e.preventDefault()
    }
  }
  
})

See working fiddle: https://jsfiddle.net/floorish/zhyqgqpz/
The <router-link> component checks e.defaultPrevented here : https://github.com/vuejs/vue-router/blob/dev/src/components/link.js#L49 , but that is still false when it checks the condition.

Only workaround I've found is to capture the event on a parent element (included in fiddle).

Vue version: 2.0.7
Vue-router version: 2.0.2
Possibly related to: #390

@yyx990803
Copy link
Member

I think we will add a disabled prop to <router-link> in the next feature release.

@floorish
Copy link
Author

floorish commented Nov 18, 2016

I don't think that'll solve the following use case:

<router-link to="/next" @click.native="confirm">next</router-link>

const app = new Vue({
  router,
  methods: {
    confirm: function(e) {
        if ( ! window.confirm('Are you sure?')) {
          e.preventDefault()
        }
        // else continue to route
    } 
  }
})

@LinusBorg
Copy link
Member

LinusBorg commented Nov 18, 2016

@floorish that should rather be taken care of in the beforeRouteLeave hook, I think.

@floorish
Copy link
Author

floorish commented Nov 18, 2016

@LinusBorg
Well you can't if you're routing to the same route, but different param, e.g.:

You're on /1

const Component = {
  template: '<div><router-link to="/2" >next</router-link></div>',
  beforeRouteLeave: function(to, from, next) {
        // not executed when route stays the same, but only params change
    next(false)
  }
}

const routes = [{
  path: '/:id',
  component: Component,
}]
const app = new Vue({
  router,
})

Which is also something that changed from vue-router 1 => vue-router 2
In this case watch: '$route' does not make sense, since it's triggered after the route change.

@LinusBorg
Copy link
Member

LinusBorg commented Nov 18, 2016

That is indeed the case currently, but that real problem here is that the hooks are not called, so the issue should be resolved in that realm (and I think there is an open issue about it but I can't seem to find it^^).

Shoe-horning a workaround into <router-link> does not appear to be not a proper solution to me.

@floorish
Copy link
Author

floorish commented Nov 18, 2016

You're right—it's a related, but different issue.

Still I'd rather have the custom click event execute before the <router-link> event in the case that you have multiple <router-link>s.

<router-link to="/dashboard" >back</router-link>
<router-link to="/2" @click.native="confirm" >next</router-link>

This reads more natural to me than checking the to param in the beforeRouteLeave hook and branch there.

See also the discussion here: #390

@yyx990803
Copy link
Member

You can programmatically check the to route in the leave hook. Anyway, I think this is just a matter of preference and we'd rather recommend only one way of achieving it for consistency.

@floorish
Copy link
Author

@yyx990803 You can't check the to route in all situations:

<router-link to="/dashboard" >cancel</router-link>
<router-link to="/dashboard" @click.native="save" >save</router-link>

But I guess in that case you don't use <router-link> at all and let the custom click handler route to the next step?

@fnlctrl
Copy link
Member

fnlctrl commented Nov 29, 2016 via email

@vkruoso
Copy link

vkruoso commented Apr 26, 2017

I have a use case where the user must be logged in to access the target page. If the user is not logged in, a modal will show up and the route navigation should be avoided. I could use a button or something else that is not an a tag and use router.push, but that prevents the browser to open the page on a new tab correctly. I ended up using the event prop to prevent the router-link to listen to the click event. Here is a sample:

<router-link :to="{ name: 'my-favorites' }"
             :event="''"
             @click.native.prevent="routeOrLogin({ name: 'my-favorites' })">
  Favorites</router-link>

Where the routeOrLogin method will check if the user is logged in and redirect if so, using router.push, or open the login modal otherwise.

@mbraint
Copy link

mbraint commented Aug 4, 2017

@vkruoso good descision!
In my case i simplify it like that:

export default {
  props: {
    disabled: Boolean
  }
}
<router-link :to="/foo"
             :event="disabled ? '' : 'click'">
  Foo</router-link>

@tayanefernandes
Copy link

@yyx990803 When will we have the disabled prop to <router-link>?

@gvdonovan
Copy link

gvdonovan commented Oct 2, 2017

Router Link Changes when route is cancelled via component hook. I have an app with tabs - each tab is a router-link. When I navigate from a route that has unsaved data I save the data and prevent the route from proceeding in the event there are errors via the component hook. However, the link the user clicked on is still set to the active route - what am I missing?

Here's the component code:

beforeRouteLeave: function (to, from, next) {
            var self = this;
            self.save( { callback: proceed } );

            function proceed() {
                if (self.validationErrors.length === 0) {
                    next();
                } else {
                    next(false);
                }
            }
        },

@vkruoso
Copy link

vkruoso commented Oct 2, 2017

@gvdonovan I don't think that the router hook is a great place to add that kind of logic. To me it would be better to add that logic in the component that actually holds all the tabs: when the route changes, validate data. If there is any errors replace the route with the one of the tab with validation errors.

The fact that you are calling next(false) should prevent you from navigating to the next route and if should work. Maybe that deserves a separate issue.

@gvdonovan
Copy link

@vkruoso - thanks for the quick response. I thought putting the check for dirty changes in the component hook was appropriate because I didn't want the parent view that contains the tabs to know anything about the components. Calling next(false) prevents the route from changing but the tab that was clicked on is set to active. I could hack this with jquery but would rather not do that :)

@gvdonovan
Copy link

OK... the problem was that bootstrap tabs took over. When removed the data-toggle/role attributes it all works as expected.

@Devric
Copy link

Devric commented Oct 16, 2017

I had a slightly use case.

I need to allow

  • ctrl/cmd + click to open new Tab, otherwise open as a popup.

@gvdonovan your solution partially worked, but it disabled the ability to ctrl/cmd + "click".
Any nicer way to achieve this or do need to programmatically detect in click event

@bmarkovic
Copy link

bmarkovic commented Mar 9, 2018

Why is this issue closed?

I don't agree that adding a hook just to be able to disable a router link (which would work for buttons in normal HTML) is an acceptable solution, except for the interim. I urge you to reconsider the 'disabled' attribute support.

Not to mention that these hooks have no access to the VM and by extension, to application state (which is by far the most likely source of information on whether or not to disable one route) unless one specifically imports global state into the component (which is another batch of dirt, because best practices insist on injecting Vuex and using it through this.$store elsewhere).

Edit: I hope you'll at least consider my PR. I think it's not much of a breaking change, as those that use disabled for styling purposes on <router-link> and then had to resort to something like :event to prevent link activation would see no difference (as the change would have no chance to surface), and hardly anyone uses disabled on elements without a desire to actually disable them.

@ColinLaws
Copy link

+1 for disabled attribute, I really feel that is a simple and easy way to prevent navigation via markup.

@bmarkovic
Copy link

I don't think my PR will be merged as there were subsequent patches that were merged or at least got comments and fix requests. I understand that the patch was trivial but the demand was/is real, so this is obviously a design decision by the core team (they do not want this particular option in the router).

I understand that this software needs to meet a common ground between many personal "technical itches" and given how great the Vue experience is due to their adherence to this principle I fully support whatever they choose. The only thing I dislike about it is that there was absolutely no feedback from the powers that be maintainers (even a "sod off, we're not interested" would be nice, let alone a rationale).

@stuntpig
Copy link

Has the disabled prop been added, or going to be added? It seems really odd to us that a navigation component does not allow you to directly disable a link.

@AppSynergy
Copy link

Fake disabled prop:

:class="(isDisabled(thing) ? 'disabled' : '')"
:event="(isDisabled(thing) ? '' : 'click')"

instead of:

:disabled="isDisabled(thing)"

@endyquang
Copy link

I just encountered this issue.
My solution is quite simple.
Just wrap the router-link outside of a div, make sure the div span all the width and height of the router-link then finally set @click for the div

<router-link to="/abc">
    <div  style="width: 100%; height: 100%;" @click="onClick" />
</router-link>

& the method:

onClick(event) {
    if (blah blah) event.preventDefault()
}

@mingoes
Copy link

mingoes commented Nov 19, 2018

Based on the answer of @AppSynergy
I used <router-link event=""> to disable the click event.

@Menighin
Copy link

Just ran into this. Using the same workaround, but a disabled would be much more classy, imo.

@y-nk
Copy link

y-nk commented Dec 17, 2018

<router-link event=""> works but seems wrong on an idealogical level. it's highly unnatural both in the reading and the understanding of the result. I honestly don't understand how the Vue.js crew who mainly focus on readability and simplicity of code would find this "acceptable".

@bmarkovic
Copy link

The PR was marked as "intend to implement" a month or so ago.

@vuejs vuejs deleted a comment from simonwep Apr 15, 2019
@vuejs vuejs deleted a comment from dietrichg Apr 15, 2019
@vuejs vuejs deleted a comment from avxkim Apr 15, 2019
@vuejs vuejs deleted a comment Apr 15, 2019
@vuejs vuejs deleted a comment from vsg24 Apr 15, 2019
@vuejs vuejs deleted a comment from cerw Apr 15, 2019
@vuejs vuejs deleted a comment from NoraGithub Apr 15, 2019
@vuejs vuejs deleted a comment from camslice Apr 15, 2019
@vuejs vuejs deleted a comment from homerjam Apr 15, 2019
@OzanKurt
Copy link

@yyx990803 Any updates about this? There is a pretty cool Pull Request above.

@caiokawasaki
Copy link

The Beatles band would say: Help!

@cedric25
Copy link

Just suggesting another workaround with the CSS rule 'pointer-events: none;':

<router-link :to="{ name: '...' }" :disabled="..." class="my-link">
  Click
</router-link>
.my-link[disabled] {
  pointer-events: none;
}

@y-nk
Copy link

y-nk commented May 22, 2019

@cedric25 that's not an enforced solution since one just need to remove that css rule to get access to the link ; PR is about deleting the capability to follow the link programmatically

@cedric25
Copy link

With the 'event' tricks, one just needs to look at the link 'href' attribute to get access to the link, or did I miss something? (if you haven't overridden 'tag')

Play with the ':disabled' condition and yes it is programmatic.

@jagreehal
Copy link

I've gone for the 'disabled' solution. I think there should be an official solution.

@ElVisPL
Copy link

ElVisPL commented Jul 23, 2019

'disabled' won't work if tag prop is set to e.g div

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests