Skip to content
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

Force navigation when clicking on a router-link even if the route doesn't change #974

Open
DevanB opened this issue Dec 2, 2016 · 32 comments
Labels
feature request has workaround needs RFC This feature request needs to go through the RFC process to gather more information

Comments

@DevanB
Copy link

DevanB commented Dec 2, 2016

Hey there. This is more of a question than a bug, but could turn into a feature request, if there is no way to currently handle this.

tl;dr; - Is there some way for me to use a global hook and run some code regardless if the current page is the page that will be navigated too?

I'm trying to call some methods (Vuex commits actually) in the vue-router beforeEach or afterEach hooks. These methods do things like closing the navigation, which need to be ran when clicking every link in the application. Everything works like a charm, until the user clicks a link to the page they are currently on. It seems (as it should) that the page doesn't actually change, but also that none of the hooks are called.

Thanks!

@posva
Copy link
Member

posva commented Dec 9, 2016

It makes sense not to call the hooks because there's no navigation since it's the same route.
@DevanB What code do you need to run on that case?

Edit: The easiest workaround to trigger a new navigation is to include a fake query or hash so the route is indeed different and the navigation isn't aborted, e.g. $router.push({ query: { ...$route.query, t: Date.now() }})

@posva posva changed the title Running code on beforeEach or afterEach hooks regardless if current page or not Force navigation when clicking on a router-link even if the route doesn't change Dec 9, 2016
@DevanB
Copy link
Author

DevanB commented Dec 9, 2016 via email

@posva
Copy link
Member

posva commented Dec 10, 2016

You can also create a custom component that binds the right vuex action to the click event on top of using a router-link element

@DevanB
Copy link
Author

DevanB commented Dec 10, 2016

Didn't think about that! I will give it a shot. Thanks for the suggestion! Keep up the great work! Maybe one day I'll have the pleasure of being a collaborator on Vue; really enjoying working with it.

@DevanB DevanB closed this as completed Dec 10, 2016
@DevanB
Copy link
Author

DevanB commented Dec 10, 2016

Just an update for any people that may want to solve this in a similar manner. I solved it by creating, importing and using this component, instead of router-link:

RouterLink.vue

<script>
  import { mapActions } from 'vuex'
  export default {
    methods: {
      ...mapActions([ 'closeIsiAndNavigation' ])
    },
    name: 'RouterLink',
    props: {
      to: {
        type: String,
        required: true
      },
      tag: {
        type: String,
        required: false,
        default: 'a'
      }
    },
    render (h) {
      return (
        <div onClick={ this.closeIsiAndNavigation }>
          <router-link to={ this.to } tag={ this.tag }>{ this.$slots.default }</router-link>
        </div>
      )
    }
  }
</script>

Again, thanks for the suggestion @posva. It's working beautifully.

@ByScripts
Copy link

IMO, Vue Router should respect the "default" behavior of any "normal" link.

On a simple HTML page, when clicking on the link of the page we already are on, the page reload. And I think Vue Router should not alter this default behavior, because it's what most user using the web is expecting.

@lucianobosco

This comment has been minimized.

@dominik-bln
Copy link

dominik-bln commented Oct 11, 2019

I just made a similar comment in #2963, but it seems relevant here as well (and also this issue is still open 😉 ). I believe there are three different use-cases hidden behind a duplicate navigation:

  1. reload (as reported in the current issue)
  2. reject (as is the current behaviour as of feat: return a promise with push and replace #2862)
  3. ignore(as in Make NavigationDuplicated a no-op for push and replace #2963 and Throw Error "NavigationDuplicated" #2872 and was the behaviour before feat: return a promise with push and replace #2862)

My suggestion is to make this configurable like so:

const router = new Router({
    routes: [],
    duplicateNavigationPolicy: 'reload' // other options: 'ignore' and 'reject'
})

I'd be happy to try and open a PR over the weekend, if this is something that would be considered.

Edit: Changed the suggested configuration value for the current behaviour to reject

@vuejs vuejs deleted a comment from RickKukiela Oct 17, 2019
@theoephraim
Copy link

+1 -- this seems like a common problem for a burger-button type mobile navigation that takes up the whole screen. Everything works until the user clicks the link for the current page. Rather than implementing a custom wrapper component or handling native DOM clicks, it seems like I should be able to listen to the router for some type of hook that will always fire regardless of where the router-link leads to.

Not quite the same as what @dominik-bln suggested above. I would recommend the router always firing an event, on each navigation "attempt" regardless of outcome.

@ahazelwood
Copy link

ahazelwood commented Nov 12, 2019

This is the exact issue I'm currently trying to see if there is a solution to. Ideally route-link already has the ability to do exact matching to set the active class. So in the case, if exact is specified, or as mentioned, allow the user re-clicks the same link, the router should be configurable. I'm still learning vue, so I'd be happy to see what @dominik-bln was able to come up with.

Without this capability, there is no way to force the route to be accepted in overrides like beforeEach, which currently causes a recursive stack overflow.

My example is something like the following:

<router-link to="/nav?url=url1.com" exact exact-active-class="active" ><span class="mdi mdi-bing"></span ></router-link> <router-link to="/nav?url=url2.com" exact exact-active-class="active" ><span class="mdi mdi-earth"></span ></router-link>

In this case, the url parameter is different but the route path is the same (resulting in a fullPath difference, but not a path difference).

@dominik-bln
Copy link

I didn’t do anything here yet, as I’d like to get feedback from a maintainer first on whether a PR like this would even be considered.

@kicktipp
Copy link

I also think this behavior is really annoying. I often click a link again, to reload the news, restart the form input or just start from scratch. Vue should mimic the default browser behavior or at least it should be configurable. I can't imagine a reason why it is implemented the way it is. The user clicks a link, so the user wants obviously something to happen.

At the moment it is very difficult to manage. Many solutions are taking about @click.native but this doesn't work as the router operation is executed first and then the click handler. I ended up with some pages being loaded twice at each click

What I came up with is the following:
1.
<router-view :key="this.$store.state.ui.navigationClicked"></router-view>
2. And a custom Component with a mousedown event firing before the router executes:

     <router-link
                @mousedown.native="mousedown"
                ...

      mousedown() {
                if (this.$route.path === this.path) {
                    this.$store.commit("navigationClicked");
                }
            },  

I really think this should be fixed inside Vue.js.

@Sergio1C
Copy link

@kicktipp you solution that writted above is working? Could you explain it more exactlies? We are catching event and calling vue router hook manually in $store internally?

@kelunik
Copy link

kelunik commented Feb 3, 2020

I'd also like to see this implemented or implement it myself and provide this as a PR. I'd like it to do a data reload and scroll to the top again (depending on the custom scroll behavior), as clicking a link and doing nothing is a really bad user experience.

I guess there should be a global hook and per-component hooks?

@kelunik
Copy link

kelunik commented Feb 3, 2020

See #3114 for a possible implementation. As long as that PR isn't merged, it can still be accomplished by forking the RouterLink component. I suggest a wrapper component that implements the :on-abort handler automatically for each link and maintains the state somewhere, probably in the root component?

@posva posva added the fixed on 4.x This issue has been already fixed on the v4 but exists in v3 label Feb 28, 2020
@atmatthewat
Copy link

atmatthewat commented Jun 16, 2020

Would be helpful to have a recommendation here in this issue for how to solve this use case generally prior to 4.x.

In my application, like others that have been described, I have a navigation pane (v-navigation-drawer with v-list) with links (each v-list-item has a 'to' property) and need each link to always fetch the latest data from the server, including re-clicking the same link we are on now.

The current loader is triggered by a watcher on $route() as well as when created() is fired. A v-on:click method fires after navigation, so it isn't possible to tell whether navigation happened or not, and I don't want to redo the loading from the server if it did.

@n10000k
Copy link

n10000k commented Aug 28, 2020

I agree I think something is needed rather than waiting for the mean time.

The only thing I can suggest is watch for the route change.

@posva posva added needs RFC This feature request needs to go through the RFC process to gather more information and removed fixed on 4.x This issue has been already fixed on the v4 but exists in v3 labels Sep 24, 2020
@macneiln
Copy link

macneiln commented Jan 8, 2021

@posva: You added the "has workaround" tag but what is the recommended workaround for this?

I have a partial workaround I've used for situations involving form submission based on response details but I would prefer a one size fits all straightforward approach as mentioned in #2430 or above (details listed below).

I think the best approach would be to make <router-link> link agnostic, i.e. if the user clicks on a link the router should not care about the link destination and instead perform all the standard operations to navigate to that route such as navigation guards, mounting, beforeRouteEnter, beforeRouteUpdate, etc. just as is the case if the user clicks F5 to reload the whole page. It should be up to the developer to disable or not provide a link if the user should not click it.

==============================================================================

Partial Workaround:

image

==============================================================================

Potential Solution described in #2430:

image

==============================================================================

Potential Solution described in #974 (above):

image

@MikeFP
Copy link

MikeFP commented Feb 11, 2021

This is extremely relevant. Currently, I'm basically forced to put a "Back to top" link in the page's footer and always end up with at least one dead link to the same page. It would be really useful if we could just listen for changes to the same route somehow.

@adamsol
Copy link

adamsol commented Mar 24, 2021

This really should be addressed. Vue Router is currently incompatible with how the web works. Gmail, YouTube, Facebook, GitHub -- they all perform some action when a link that is already active is clicked; some of them even completely reload the view, just like non-SPA sites do. Ignoring active links by default in Vue Router could be a premature optimization, but the lack of a configuration option is a significant flaw.

Edit: I've implemented my own router library to circumvent this: https://github.com/adamsol/vue-pocket-router.

@flyskyko
Copy link

flyskyko commented Apr 8, 2021

I made a small plugin.
https://www.npmjs.com/package/vue-route-key

@xinxuantech
Copy link

xinxuantech commented Jul 14, 2021

I created a redirecting page to do this.

image

@patchthecode
Copy link

so is update going to be done or not?

The are legitimate use cases in loading the exact same URL but with different underlying params.

@b-3-n
Copy link

b-3-n commented Jan 28, 2022

For vue-router 4 there seems to be an undocumented API: <router-link :to="{path:'/mypath', force: true}"> (source).

@n10000k
Copy link

n10000k commented Jan 28, 2022

@posva was this fixed in v4 and not documented?

@rightaway
Copy link

Is the force api working for anyone in vue-router 4?

@posva Is it fixed in 4?

@iskyd
Copy link

iskyd commented Jul 29, 2022

force api is not working for me with vue-router 4.0.3

@kburisma
Copy link

works for me if router-view has :key with unique value
"vue-router": "4.0.12"

@posva
Copy link
Member

posva commented Nov 18, 2022

There is now an RFC that solves this issue through a higher level feature: Data Loaders: vuejs/rfcs#460

In particular, the refresh() method allows to call fetching again. If you are interested in this feature, please, check the mentioned RFC to give feedback 🙏

@Mini-ghost
Copy link

I have a simple idea to solve this feature and maybe more flexible, I wrote my idea at this RFC: vuejs/rfcs#483

This RFC provides a router lifecycle that allows us to do something when the user navigates to the same route as the current page, through this lifecycle we can have more flexibility to update specific data without reloading all pages.

If this feature can help to the situation you are facing or if you have more suggestions for this RFC, welcome to feedback.

@ttntm
Copy link

ttntm commented Sep 20, 2023

works for me if router-view has :key with unique value "vue-router": "4.0.12"

Still working in 4.2.4

Also seems like it was documented in the meantime: https://router.vuejs.org/api/interfaces/RouteLocationOptions.html#force

@RickKukiela
Copy link

Woohoo! IT seems this feature is finally added yes? We can probably close this issue then!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request has workaround needs RFC This feature request needs to go through the RFC process to gather more information
Projects
None yet
Development

No branches or pull requests