Error: Unable to Navigate to Present Location ("/search") as It is Duplicated

I keep encountering the error message NavigationDuplicated when I attempt to perform multiple searches. My search function is located in the navbar, and it's configured to capture the input value using a model. Subsequently, this value is passed as a parameter to the ContentSearched component where it is processed.

I understand that the correct approach involves using an emitter, but I'm still unsure about how to utilize it effectively. The emit function can be accessed with context.emit('', someValue)

NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated", message: "Navigating to current location ("/search") is not allowed", stack: "Error↵    at new NavigationDuplicated (webpack-int…node_modules/vue/dist/vue.runtime.esm.js:1853:26)"}

NavBar.vue

<template>
  <nav class="navbar navbar-expand-lg navbar-dark bg-nav" v-bind:class="{'navbarOpen': show }">
    <div class="container">
      <router-link to="/" class="navbar-brand">
        <img src="../assets/logo.png" alt="Horizon Anime" id="logo">
      </router-link>

      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" v-on:click.prevent="toggleNavbar">
        <span class="navbar-toggler-icon"></span>
      </button>

      <div class="collapse navbar-collapse" id="navbarSupportedContent" v-bind:class="{'show': show }">
        <ul class="navbar-nav mr-auto">
          <li class="nav-item">
            <router-link class="nav-link" to="/" ><i class="fas fa-compass"></i> Series</router-link>
          </li>
          <li class="nav-item">
            <router-link class="nav-link" :to="{name: 'EpisodesSection'}" ><i class="fas fa-compact-disc"></i> Episodios</router-link>
          </li>
          <li class="nav-item">
            <router-link class="nav-link" :to="{name: 'MovieSection'}" ><i class="fas fa-film"></i> Peliculas</router-link>
          </li>
        </ul>
        <div class="search-bar">
          <form class="form-inline my-2 my-lg-0">
            <input class="form-control mr-sm-2" v-model="query" type="search" placeholder="Buscar películas, series ..." aria-label="Search">
            <button class="btn btn-main my-2 my-sm-0" @click.prevent="goto()" type="submit"><i class="fas fa-search"></i></button>
          </form>
        </div>
      </div>
    </div>
  </nav>
</template>

<script>
  import {value} from 'vue-function-api';
  import {useRouter} from '@u3u/vue-hooks';

  export default {
    name: "NavBar",
    setup(context){
      const {router} = useRouter();
      const query = value("");

      let show = value(true);
      const toggleNavbar = () => show.value = !show.value;      
      
      const goto = () =>{
        let to = {name: 'ContentSearched' , params:{query: query}}
        router.push(to);
      };
        
      return{
        show,
        toggleNavbar,
        goto,
        query
      }
    }
  }
</script>

ContentSearched.vue

<template>
   <div class="container">
     <BoxLink/>
    <main class="Main">
      <div class="alert alert-primary" role="alert">
        Resultados para "{{query}}"
      </div>
      <div v-if="isLoading">
        <!-- <img class="loading" src="../assets/loading.gif" alt="loading"> -->
      </div>
      <div v-else>
        <ul class="ListEpisodios AX Rows A06 C04 D02">
          <li v-for="(content, index) in contentSearched" :key="index">
            <div v-if="content.type === 'serie'">
              <Series :series="content"/>
            </div>
            <div v-if="content.type === 'pelicula'">
              <Movies :movies="content"/>
            </div>
          </li>
        </ul>
      </div>
    </main>
  </div>
</template>


<script>
  import {onCreated} from "vue-function-api"
  import {useState , useRouter , useStore} from '@u3u/vue-hooks';
  import BoxLink from "../components/BoxLink";
  import Movies from "../components/Movies";
  import Series from "../components/Series";

  export default{
    name: 'ContentSearched',
    components:{
      BoxLink,
      Movies,
      Series
    },
    setup(context){
      const store = useStore();
      const {route} = useRouter();

      const state = {
        ...useState(['contentSearched' , 'isLoading'])
      };

      const query = route.value.params.query;

      onCreated(() =>{
        store.value.dispatch('GET_CONTENT_SEARCH' , query.value);
      });
      return{
        ...state,
        query,
      }
    }
  };
</script>

Answer №1

I encountered this issue when using a router-link that pointed to the same route, for example /products/1.

Users could click on products, but if a product was already clicked and the component view was already loaded, attempting to click it again would trigger an error or warning in the console.

For more information, you can check out the discussion on this GitHub issue.

Posva, a key contributor to vue-router, recommends:

router.push('your-path').catch(err => {})

Alternatively, if you prefer not to have an empty catch block, one solution is to compare the router navigation with the current route and only navigate if they are different:

const path = `/products/${id}`
if (this.$route.path !== path) this.$router.push(path)

Keep in mind that $route is a special object provided by vue-router to every component. More details can be found in The Route Object.

Answer №2

In my opinion, the most effective solution for this issue is to address it at the root level without relying on Router.push as an asynchronous call.

import Router from 'vue-router';

const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
};

Vue.use(Router);

Answer №3

For those who may not be fully confident in capturing various errors, here is a more thoughtful approach to the implementation:

this.$router.push("route").catch(err => {
  if (err.type != "DuplicateNavigation") {
    throw err;
  }
});

Answer №4

Looking ahead to 2021, the worldwide setup :

I made a specific adjustment to address the persistent NavigationDuplicated error, as leaving a catch empty can lead to potential risks. Here's what I did :

const router = new VueRouter({/* ... */})

const originalPush = router.push
router.push = function push(location, onResolve, onReject)
{
    if (onResolve || onReject) {
        return originalPush.call(this, location, onResolve, onReject)
    }
 
    return originalPush.call(this, location).catch((err) => {
        if (VueRouter.isNavigationFailure(err)) {
            return err
        }
   
        return Promise.reject(err)
    })
}

Add this snippet during vue-router initialization.
Credit goes to @Oleg Abrazhaev for the suggestion.

Answer №5

While looking for a solution, I encountered the same issue and found a workaround by including timestamp in the this.$route.query parameters on the search page.

this.$router.push({
    path: "/search",
    query: {
      q: this.searchQuery,
      t: new Date().getTime(),
    }
  });

I hope this suggestion proves to be helpful for you as well.

Answer №6

When implementing router.push in your code and disregarding the possibility of navigation failure, it is recommended to handle potential errors using catch:

router.push('/location').catch(err => {})

Answer №7

When referring to the manual:

router.push(location, onComplete?, onAbort?)

An easier alternative can be:

router.push("/", () => {});

Answer №8

It seems like you are mixing various concepts here, from using router-links to programmatic navigation, query params, and a state store. This can make it tricky to determine the best solution for your situation.

However, I believe the most suitable approach for you would be to:
1) Define your route as

{
  path: "/search/:searchString",
  component: MySearchComponent,
  props: true
}

2) Opt for a responsive <router-link> instead of using router.push

<input type="text" v-model="searchString">
<router-link :to="'/search/'+searchString" tag="button">search</router-link>

3) Access the searchString in your search component by using props: ['searchString'] and referencing it with this.searchString

props: ['searchString'],
...
computed: {
  msg() {
    return `Searching for, ${this.searchString}!`;
  }
}

For a complete example, check out: https://codesandbox.io/s/vue-routing-example-9zc6g
Please note that I simply forked the first codesandbox I found with a router, so adjust accordingly.

Answer №9

To implement this in TypeScript, follow these steps:

const customPush = VueRouter.prototype.push

VueRouter.prototype.push = async function push(location: RawLocation): Promise<Route> {
  try {
    return await customPush.bind(this)(location)
  } catch (error) {
    if (error?.name === 'NavigationDuplicated') {
      console.warn(error)
      return error
    } else {
      throw error
    }
  }
}

Answer №10

Although I may be fashionably late to the occasion, I wanted to share my unique solution to this problem that hasn't been mentioned yet: I decided to create a middleman search page that acts as a gateway to the search results. This page now serves as a hub for preprocessing the search terms before displaying the final results.

The design of the page is quite simple:

<template>
  <div>searching ...</div>
</template>  

By implementing this approach, I successfully eliminated the NavigationDuplicated error. Additionally, by handling the fetch operation on this intermediate page, I have effectively isolated error-handling responsibilities from both the search bar and the results display, providing an added layer of organization and efficiency.

Answer №11

Sharing the solution I discovered here because I struggled to find clear documentation on it and had to figure it out through trial and error. My interpretation of vue-router guards may be corrected by someone or prove useful to others.

This solution utilizes vue-router V4.x and a global beforeEach guard.

The scenarios covered are:

  1. User accesses https://app.com/ without authorization;
  2. User accesses https://app.com/ with prior authorization;
  3. User accesses any route, regardless of authentication requirements.

Routes:

const routes = [
  /**
   * Routes not requiring authentication
   */
  {
    path: '/',
    component: () => import('layouts/NotAuthorizedLayout.vue'),
    children: [
      {
        path: 'login',
        name: 'LOGIN',
        component: () => import('pages/Login.vue') 
      },
      {
        path: 'emailsignup',
        component: () => import('pages/EmailSignup.vue') 
      },
      {
        path: 'forgottenpassword',
        component: () => import('pages/ForgottenPassword.vue') 
      }
    ]
  },

  /**
   * Routes requiring authentication
   */
  {
    path: '/',
    component: () => import('layouts/AuthorizedLayout.vue'),
    meta: { requiresAuth: true },
    children: [
      { 
        path: 'authors',
        name: 'AUTHORS',
        component: () => import('pages/Authors.vue') 
      },
      { path: 'profile', component: () => import('pages/userProfile/index.vue') }
    ]
  }
];

Global beforeEach Guard:

  const redirectToLogin = route => {
    const LOGIN = 'LOGIN';
    if (route.name != LOGIN) {
      return { name: LOGIN, replace: true, query: { redirectFrom: route.fullPath } };
    }
  };

  const redirectToHome = route => {
    const DEFAULT = 'AUTHORS';
    return { name: DEFAULT, replace: true };
  };

  Router.beforeEach((to, from) => {
    const userIsAuthenticated = store.getters['authentication/userIsAuthenticated'];
    const requiresAuth = to.matched.some((route) => route.meta && route.meta.requiresAuth);

    if (!userIsAuthenticated && to.fullPath === '/') {
      return redirectToLogin(to);
    }

    if (!userIsAuthenticated && requiresAuth) {
      return redirectToLogin(to);
    }

    if (to.fullPath === '/') {
      return redirectToHome(to);
    }

    return true;
  });

Answer №12

Your inquiry seems to be from a while ago.

The issue lies with the use of "@click.prevent". This command is not functioning correctly because your button is set as a submit button, causing the event to trigger twice.

Consider using "@submit.prevent" instead (or adjust the type of your button) for it to work properly.

Answer №13

It is recommended to follow this approach:

import { isNavigationFailure, NavigationFailureType } from 'vue-router/src/util/errors';    
this.$router.push(...).catch((error) => {
      if (!isNavigationFailure(error, NavigationFailureType.duplicated))
        throw error
    );
  }

To learn more, check out the Vue Router documentation

Answer №14

By incorporating a combination of extending the prototype with a check for the Navigation Duplicated Error, I have managed to address various errors and warnings effectively. Following a week in production, there have been no occurrences of the NavigationDuplicated error and the system is functioning smoothly.

import { equals } from 'ramda'

export function integrate(Vue) {
  const routerPush = Router.prototype.push
  const routerReplace = Router.prototype.push

  const isNavigationDuplicated = (currentRoute, nextRoute) => {
    const { name: nextName, params: nextParams = {}, query: nextQuery = {} } = nextRoute
    const { name, params, query } = currentRoute

    return equals(nextQuery, query) && equals(nextParams, params) && equals(nextName, name)
  }

  Router.prototype.push = function push(location) {
    if (!isNavigationDuplicated(this.currentRoute, location)) {
      return routerPush.call(this, location)
    }
  }

  Router.prototype.replace = function replace(location) {
    if (!isNavigationDuplicated(this.currentRoute, location)) {
      return routerReplace.call(this, location)
    }
  }

  Vue.use(Router)
}

Answer №15

Upon investigation, I discovered that an error occurs when attempting to substitute a URL query parameter with the same value.

In my scenario, I have select filters where the URL query string parameters are synchronized with their corresponding values. Everything functions smoothly when switching to a new value. However, if the value remains unchanged (such as navigating back from history) and the query string parameter is replaced with the same value, an error is triggered.

The solution I found was to implement a check to determine if the value has changed before replacing the query parameter in the router:

let newValue = 'foo'; // specify the new query value for the parameter
let qcopy = { ...this.$route.query }; // create a copy of the current query
// To prevent NavigationDuplicated error: Avoid redundant navigation to the current location
if (qcopy['your_param'] != newValue){
  qcopy['your_param'] = newValue;
  this.$router.replace({query: qcopy});
}

Answer №16

Preventing Click Propagation to Router Actions

I have a unique perspective on how to handle click propagation in relation to router actions. While my answer may not directly address the original question, it offers valuable insight that can be beneficial to others.

In my opinion, it is best to avoid making changes to global configurations or trying to intercept router logic. Instead of cluttering your application with exceptions, consider a more streamlined approach.

For instance, imagine a scenario where you have filters within a results view presented as router links. You may want these filters to behave like links without triggering actual router actions when clicked.

The solution is to intercept the click event before it reaches the router action!

This way, you can achieve the desired outcome:

Search filters (presented as links) that...

  1. Perform specific logic within a view
  2. Retain the benefits of being displayed as links (e.g., improved bot scans, user convenience, accessibility)

Technique:

Utilize @click.prevent on a child element within the router-link to prevent the click event from reaching the router.

Example:

Before: Router actions are triggered unnecessarily even within the current route

<router-link class="nav-link" :to="{name: 'MovieSection'}" >
    <i class="fas fa-film"></i>Movies
</router-link>

After: Router actions are prevented, and alternative logic (applyFilter) is executed

<router-link class="nav-link" :to="{name: 'MovieSection'}" >
    <div @click.prevent="myFunc(something)">
        <i class="fas fa-film"></i>Movies
    </div>
</router-link>

By adopting this method, you can avoid unnecessary complexity and high-level exceptions in your application.

Answer №17

In the initialization of router/index.js, I found this solution to be effective. By utilizing the call() method, you have the ability to handle various exception types or customize messages to either bypass specific conditions or trigger particular ones.

Make sure to check out

//HANDLE ROUTE ERRORS HERE

for detailed insights.

import Vue from "vue";
import VueRouter from "vue-router";

import routes from "./routes";

Vue.use(VueRouter);

/*
 * If SSR mode is not in use, the Router instantiation can
 * be exported directly;
 *
 * The function below can also be async; utilize
 * async/await or return a Promise that resolves
 * with the Router instance.
 */

export default function(/* { store, ssrContext } */) {
  const router = new VueRouter({
    scrollBehavior: () => ({ x: 0, y: 0 }),
    routes,

    // Ensure these are retained as they are and update in quasar.conf.js instead!
    // quasar.conf.js -> build -> vueRouterMode
    // quasar.conf.js -> build -> publicPath
    mode: "history"
  });

  router.beforeEach((to, from, next) => {
    if (to.meta.requiresAuth) {
      var token = window.localStorage.getItem("token");
      if (token != null) {
        var decodedtoken = JSON.parse(atob(token.split(".")[1]));
        var role =
          decodedtoken[
            "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
          ];
        if (role != "Admin") {
          next("/accessdenied");
        } else {
          next();
        }
      } else {
        next("/login");
      }
    } else {
      next();
    }

    if (to.meta.requiresLogin) {
      var token = window.localStorage.getItem("token");
      if (token != null) {
        next();
      } else {
        next("/login");
      }
    }
  });


 //Handle route errors in this section
  const originalPush = router.push;
  router.push = function push(location) {
    return originalPush.call(this, location).catch(err => {
      console.warn("router>index.js>push()", err);
    });
  };

  return router;
}

Borrowed inspiration from @Abhishek Shastri's response

Answer №18

Check out this straightforward and effective approach:

if(from.fullPath !== to.fullPath){
    return
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Pairing Video.js with vue-electron enables seamless video playlist functionality

Currently, I am working on an autoplay video playlist feature. To achieve this, I am using glob to retrieve all video absolute paths from a specific folder. Below is the code snippet that showcases my approach: let vm = this; let trailerList = null; trail ...

What is the reason FileReader does not transfer the file to loader.load() in the three.js scene?

Trying to utilize FileReader to send an ASCII file from the client side to loader.load() seems to be causing an issue. The file doesn't reach its destination. However, if I use loader.load('server path to test_file.stl') instead of loader.lo ...

When I hover over div 1, I am attempting to conceal it and reveal div 2 in its place

I need help with a CSS issue involving hiding one div on mouse hover and showing another instead. I attempted to achieve this using pure CSS, but both divs end up hidden. Would jQuery be necessary for this task? Below is the CSS/HTML code I wrote: .r ...

Is it possible to implement smooth scrolling in HTML without using anchor animation?

Is it feasible to implement a more seamless scrolling experience for a website? I'm referring to the smooth scrolling effect seen in MS Word 2013, but I haven't come across any other instances of this. I've heard that AJAX can make such th ...

Securing AJAX Requests: Encrypting GET and POST Requests in JavaScipt using Node.js

Looking for a way to secure ajax post and get requests using JavaScript. The process would involve: Server generates private and public key upon request Server sends the public key to client Client encrypts data with public key Server decrypts data wit ...

Customize Bottom Navigation Bar in React Navigation based on User Roles

Is it possible to dynamically hide an item in the react-navigation bottom navigation bar based on a specific condition? For example, if this.state.show == true This is what I've attempted so far: const Main = createBottomTabNavigator( { Home: { ...

Angular 4 is in need of CORS support

I have a server application with CORS enabled, which works well with my AngularJS client (1.x). However, I am now upgrading to Angular 4 and encountering the following error: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the rem ...

Can you identify the animation being used on this button - is it CSS or JavaScript?

While browsing ThemeForest, I stumbled upon this theme: What caught my eye were the animation effects on the two buttons in the slider ("buy intense now" and "start a journey"). I tried to inspect the code using Firebug but couldn't figure it out com ...

Arrange an array that has been sifted through

I'm having trouble figuring out where to place the .sort() method in the code snippet below. I've tried multiple placements but it either gives errors or doesn't work as intended. My goal is to sort the list alphabetically in this scenario. ...

importing files from Uploadcare using ngCordova MediaFile

I am facing an issue while attempting to upload a sound file from ngCordova's $cordovaCapture service to UploadCare. The `uploadcare.fileFrom('object')` function is failing with an 'upload' error even though I have set the public k ...

Monitoring a location within a grid using AngularJS

I have a question: How can I track the position within a 10x10 grid in AngularJS and update it using arrow keys? Initially, I considered implementing this functionality in a directive with the following code: angular.module('mymodule').directiv ...

Dynamic menu plugin for JQuery

I am still new to jQuery and currently learning how to efficiently use it in website development. I am currently working on implementing a responsive navigation plugin that I came across online. However, I have encountered an issue with the plugin where th ...

Populating Redux form fields with data retrieved from API call

Currently, I am working on an address finder component in React and I am looking for the most effective way to update the fields with values once I receive the data from the API. I am using a fetch in React to fetch the data and fill in some form fields, t ...

Incorporate a local asciinema file into an HTML document

Is there a way to embed a local asciinema session into an html file? Here is how my directory is structured: $ tree . ├── asciinema │ └── demo.cast ├── css │ └── asciinema-player.css ├── index.html ├── js │ ...

Exploring the issue of why the scroll event handler is not functioning properly within the useEffect hook

After creating a custom hook to retrieve the scroll state of an element, I noticed that the scrollHandler only triggers once. Here's my code: import { MutableRefObject, useEffect, useState } from "react" export const useScrollState = <TE ...

What is the most effective way to invoke a javascript function using AJAX?

I have a curious question while working on building a tool for my assignment. This tool involves extracting form data and JavaScript files from a website. The challenge I'm facing is that I've been instructed to call a JavaScript function using A ...

Is there a way to use JavaScript to automatically open a URL at regular intervals?

List of URLs in JavaScript: var websites = ['https://www.google.com', 'https://www.msn.com', 'https://stackoverflow.com']; I have an array containing multiple website URLs. My goal is to open each URL in a new tab or window e ...

I need a layout similar to the navbar on http://thecoloradan.com/ but I am not asking for instructions on how to do it

Have you seen the stunning navbar on this website? It appears to merge with the background upon loading, then smoothly shifts to the top of the page as you scroll. The transition is accompanied by a lovely animation. I am curious about the steps needed to ...

Modify the anchor text when a malfunction occurs upon clicking

Whenever I click on the link, I am able to retrieve the correct id, but the status changes for all posts instead of just the selected item. Below is the HTML code: <div th:each="p : ${posts}"> <div id="para"> <a style="float: right;" href= ...

Ways to create interaction between two web pages with JavaScript

I am attempting to design a webpage where one page can influence the content of another page. I have two HTML pages and a JavaScript file named "Controll.js" which contains a function that changes the content of "Indiv" in Index.html. This function is tr ...