NavigationDuplicated Перехід до поточного місця (“/ search”) заборонено [vuejs]


81

Коли я хочу зробити пошук кілька разів, це показує мені NavigationDuplicatedпомилку. Мій пошук знаходиться на панелі навігації, і спосіб налаштування пошуку полягає в тому, щоб прийняти значення за допомогою моделі, а потім передати значення як параметр компоненту ContentSearched, а потім отримати значення пошуку в цих компонентах.

Я знаю, що правильним способом є використання випромінювача, але я все ще не знаю, як навчитися його використовувати. Для доступу до випромінювання є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>

Відповіді:


97

Це трапилося зі мною, коли я router-linkвказував на той самий маршрут. напр /products/1.

Користувач може клацнути на продуктах, але якщо на продукт вже клацнули (і подання компонента вже було завантажено), і користувач намагається ще раз клацнути на ньому, на консолі відображається повідомлення про помилку / попередження.

Ви можете дізнатись більше про проблему github. .

Посва, один з основних вкладників vue-router, пропонує:

router.push ('your-path'). catch (помилка => {})

Однак, якщо ви не хочете мати catchблок, який нічого не робить, для вирішення проблеми ви можете порівняти навігацію маршрутизатора з поточним маршрутом і здійснювати навігацію лише в тому випадку, якщо вони відрізняються:

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

Примітка: $routeце об'єкт, який надається vue-router кожному компоненту. Докладніше див. У розділі Об’єкт маршруту .


33

Я думаю, що найкраще рішення цього може бути вирішене на кореневій мітці, якщо ми не будемо використовувати далі Router.pushяк асинхронний виклик.

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);

Алілуя! Хоча у мене була ця помилка навіть при використанні маршрутизатора! На Github posva стверджує, що цей обхідний шлях буде необхідний лише при використанні router.push (), але не для маршрутизатора. Будь-яка ідея, чому я все ще отримую цю помилку під час використання маршрутизатора (і позбавляюся від використання вашого коду)?
Tech Nomad

@TechNomad, ти можеш скористатися $router.beforeEach'гачком, і тим next(false)самим припинити навігацію
Saksham

@TechNomad, NavigationDuplicated генерується перед викликом $router.beforeEach.
Марк Дорнян,

26

Якщо вам не комфортно ловити всілякі помилки, я думаю, що ця реалізація є більш уважною:

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

16

Станом на 2020 рік, глобальна конфігурація:

Я хотів лише заглушити NavigationDuplicatedпомилку, порожній улов може бути небезпечним. Отже, я зробив це:

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')

Вставте це один раз під час ініціалізації vue-router.



12

якщо ви використовуєте router.push у своєму коді, і ви не дбаєте про несправність навігації, вам слід зловити його, використовуючи catch:

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

8

в керівництві:

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

Ви можете використовувати більш просто

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

Це повинен бути кращий, чистіший спосіб
Тоньйо

8

Я зіткнувся з такою ж проблемою під час пошуку. Моє рішення - додати timestampдо this.$route.queryпараметрів сторінки пошуку.

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

Сподіваюся, це допоможе вам.


Дякую, це чудова відповідь. Інші відповіді замовчують NavigationDuplicatedпомилку, але це дозволяє фактично орієнтуватися на той самий маршрут. Це дозволяє орієнтуватися по одному маршруту, але з різними параметрами. У моєму випадку мені це було потрібно для модульного тестування.
JuroOravec

4

Ви змішали тут кілька концепцій від router-links до програмної навігації та запитів параметрів до сховища стану. Це трохи ускладнює допомогу вам і підказку, яке тут "правильне" рішення.

Тим не менше, я думаю, що найкращим підходом для вас було б:
1) визначити свій маршрут як

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

2) використовуйте адаптивний <router-link>замість вашогоrouter.push

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

3) отримати доступ до searchStringвашого компонента пошуку за допомогою props: ['searchString']іthis.searchString

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

Повний приклад: https://codesandbox.io/s/vue-routing-example-9zc6g
Заметь, я просто роздвоєний перший codesandbox з routerя міг би знайти, налаштувати відповідним чином .


-1

Ось просте та ефективне рішення:

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

@ hero592 Будь ласка, прочитайте кодекс поведінки та утримайтеся від таких коментарів. Ми всі тут, щоб допомагати іншим і не віддаватися таким неприйнятним практикам.
Manish
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.