NavigationDupliziert Das Navigieren zum aktuellen Standort ("/ search") ist nicht zulässig [vuejs]

75

Wenn ich mehrmals suchen möchte, wird mir der NavigationDuplicatedFehler angezeigt. Meine Suche befindet sich in der Navigationsleiste. Ich habe die Suche so konfiguriert, dass der Wert mithilfe eines Modells verwendet und dann als Parameter an die ContentSearched-Komponente übergeben wird. Anschließend wird der Wert der Suche in diesen Komponenten empfangen.

Ich weiß, dass der richtige Weg darin besteht, einen Emitter zu verwenden, aber ich weiß immer noch nicht, wie ich lernen soll, ihn zu verwenden. Um auf die Ausgabe zuzugreifen istcontext.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>
Chris Michael
quelle

Antworten:

88

Dies passierte mir, als ich router-linkauf dieselbe Route zeigte. zB /products/1.

Der Benutzer kann auf die Produkte klicken. Wenn jedoch bereits auf ein Produkt geklickt wurde (und die Komponentenansicht bereits geladen wurde) und der Benutzer erneut versucht, darauf zu klicken, wird der Fehler / die Warnung in der Konsole angezeigt.

Sie können mehr über das Github-Problem erfahren . .

Posva, einer der Hauptverantwortlichen für Vue-Router, schlägt vor:

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

Wenn Sie jedoch keinen catchBlock haben möchten, der nichts bewirkt, können Sie zur Lösung des Problems die Routernavigation mit der aktuellen Route vergleichen und nur dann navigieren, wenn sie sich unterscheiden:

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

Hinweis: $routeist ein Objekt, das von vue-router für jede Komponente bereitgestellt wird. Weitere Informationen finden Sie unter Das Routenobjekt.

roli roli
quelle
32

Ich denke, die beste Lösung hierfür kann am Root-Label gelöst werden, wenn wir sie nicht weiter Router.pushals asynchronen Aufruf verwenden.

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);
Abhishek Shastri
quelle
Halleluja! Obwohl ich diesen Fehler auch bei Verwendung von Router-Link hatte! Auf Github posva behauptet, diese Problemumgehung wäre nur erforderlich, wenn router.push () verwendet wird, nicht jedoch für Router-Links. Irgendeine Idee, warum ich diesen Fehler immer noch bekomme, wenn ich Router-Link benutze (und wenn ich deinen Code benutze)?
Tech Nomad
@TechNomad Sie können $router.beforeEach'Hook verwenden und in diesem verwenden, next(false)um die Navigation zu beenden
Saksham
24

Wenn Sie sich nicht wohl fühlen, wenn Sie alle Arten von Fehlern abfangen, ist diese Implementierung meiner Meinung nach rücksichtsvoller:

this.$router.push("path").catch(error => {
  if (error.name != "NavigationDuplicated") {
    throw error;
  }
});
fguillen
quelle
12

Wenn Sie router.push in Ihrem Code verwenden und sich nicht darum kümmern, dass die Navigation fehlschlägt, sollten Sie ihn mit catch abfangen:

router.push('/location').catch(err => {})
Chan Yoong Hon
quelle
12

Ab 2020 globale Konfiguration:

Ich wollte nur NavigationDuplicatedFehler zum Schweigen bringen , ein leerer Fang kann gefährlich sein. Also habe ich das gemacht:

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

function patchRouterMethod (router, methodName)
{
    router['old' + methodName] = router[methodName]
    router[methodName] = async function (location)
    {
        return router['old' + methodName](location).catch((error) =>
        {
            if (error.name === 'NavigationDuplicated')
            {
                return this.currentRoute
            }
            throw error
        })
    }
}

patchRouterMethod(router, 'push')
patchRouterMethod(router, 'replace')

Fügen Sie dies einmal ein, wenn Sie den Vue-Router initialisieren.

Alexandre Daubricourt
quelle
7

im Handbuch:

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

Sie können einfacher verwenden

router.push("/", () => {});
Ruslan Semenov
quelle
Dies sollte der bevorzugte, sauberere Weg sein
Tonjo
7

Ich habe das gleiche Problem bei der Suche festgestellt. Meine Lösung besteht darin timestamp, die this.$route.queryParameter der Suchseite zu ergänzen .

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

Hoffe es hilft dir.

cisor deng
quelle
3

Sie haben hier mehrere Konzepte gemischt, von router-links bis zur programmatischen Navigation, um Parameter in einem Statusspeicher abzufragen. Das macht es ein bisschen schwierig, Ihnen zu helfen und Ihnen zu sagen, was die "richtige" Lösung hier ist.

Dennoch denke ich, dass der beste Ansatz für Sie darin besteht,
1) Ihre Route als zu definieren

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

2) Verwenden Sie ein Responsive <router-link>anstelle Ihresrouter.push

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

3) Zugriff auf die searchStringin Ihrer Suchkomponente über props: ['searchString']undthis.searchString

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

Vollständiges Beispiel: https://codesandbox.io/s/vue-routing-example-9zc6g
Hinweis: Ich habe gerade die erste Codesandbox mit einem Gabel gegabelt, den routerich finden konnte, und entsprechend angepasst .

TommyF
quelle
-1

Hier ist eine einfache und effiziente Lösung:

if(from.fullPath === to.fullPath){
    return
}
hero592
quelle
@ hero592 Bitte lesen Sie den Verhaltenskodex durch und unterlassen Sie solche Kommentare. Wir alle sind hier, um anderen zu helfen und uns nicht solchen inakzeptablen Praktiken hinzugeben.
Manish