Vue Router 4: How to add/push a new URL entry without causing routing push?

Viewed 47

I think this is the most common asked question about Vue Router but with no clear answer or suggestion to deal with it.

What I have?

1- A view with multiple components (Main, Navigation, Pagination, Filtering).

2- Using state management 'Pinia' to update the data that being shown in (Main) component whenever there is an action ( clicking a <router-link> ) in the other components.

3- a piece of the code:


<router-link 
  :to="{ 
      path: useRoute.path,
      params: { ...useRoute.params},
      query: { ...useRoute.query, page: pageNumber}
  }" 
  @click.capture.prevent="updateData(pageNumber)"
>
  <span> {{pageNumber}} </span>
</router-link>

What I get?

Update the data successfully in (Main) component but I can't update the URL query parameter (page) due using of event:

@click.capture.prevent

That's stop the default behavior of the <router-link> which is navigate to the route that specified in :to="{}"

What I DON'T want?

1- Using:

history.pushState()

will lead to cause issues to the Router because it can't track the changes those made to the URL and so a malfunction will occur within Router navigation.

2- Using:

useRouter.push()

Will lead to make a push to the route that set in push() function.

What I want?

Update the URL and Router history in the same time without causing router navigation or malfunction to the Router.

If you are interesting to see the code:

@/router/index.js


import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';

const routes = [
  {    
    name: 'home',
    path: '/home',
    alias: ['/'],
    component: HomeView
  },
  {
    name: 'store',
    path: '/store/:slug?/:subSlug?/:page?',
    component: () => import(/* webpackChunkName: "store" */ '../views/StoreView.vue'),
    props: route => ({
      slug: route.params.slug,
      subSlug: route.params.subSlug,
      page: route.query.page
    })
    
  },
  {
    name: "page-not-found",
    path: "/:pathMatch(.*)*",
    component: () => import(/* webpackChunkName: "not-found" */ "../views/PageNotFoundView.vue")
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
});

export default router

StoreView.vue


<template>

    <section class="section-content padding-y">

      <div class="container">
    
          <StoreMain />
          <PaginationComponent />

      </div>

    </section>

</template>

<script>
import StoreMain from "@/components/StoreMainComponent";
import PaginationComponent from "@/components/PaginationComponent"; 
import {usePaginationStore} from "@/store/Pagination";
import {defineProps} from "vue";
import router from "@/router";

export default {
  name: "StoreView",
  components: {
    StoreMain,
    PaginationComponent

  }
}

</script>

<script setup>

/*
  Define handlers (properties, props and computed)
*/

const props = defineProps({
  slug: {
    type: String,
    required: false
  },
  subSlug: {
    type: String,
    required: false
  },
  page: {
    type: String,
    required: true
  }
});
const storePagination = usePaginationStore();

const triggerGetDataResult = async () => {

  await storePagination.getDataResult(URLToBackend + (`?page=${props.page}`));

await triggerGetDataResult();

</script>

<style scoped>

</style>

StoreMainComponent.vue

<template>

  <div class="row">

    <main class="col-md-12" >

    <!-- Process the data stored in the Store -->
    
    </main> 

  </div>

</template>

<script>

import {usePaginationStore} from "@/store/Pagination";

export default {
  name: "StoreMainComponent"
}

</script>

<script setup>

const storePagination = usePaginationStore();
storePagination.$subscribe((mutation, state) => {});

</script>

<style scoped>

</style>

PaginationComponent.vue


<template>

  <nav class="mt-4" aria-label="Page navigation sample">
    <ul class="pagination justify-content-center">
      <li class="page-item">
        <router-link
            :to="{ path: route.path, query: { ...route.query, page: +page - 1 } }"
            class="page-link"
            @click.capture.prevent="changePage(+page - 1)"
        >
          <span> Previous </span>
        </router-link>
      </li>

      <li class=page-item" >
        <router-link
            :to="{ path: route.path, query: { ...route.query, page: +page + 1 } }"
            class="page-link"
            @click.capture.prevent="changePage(+page + 1)"
            >
          <span> Next </span>
        </router-link>
      </li>
    </ul>
  </nav>

</template>

<script>

import {usePaginationStore} from "@/store/Pagination";
import {useRoute} from "vue-router";
import {ref} from "vue";

export default {
  name: "PaginationComponent"
}

</script>

<script setup>

const storePagination = usePaginationStore();
const route = useRoute();
const page = ref(+route.query.page);

const changePage = async (pageNumber) => {

     // Change pageNumber in store which will trigger the 'getDataResult' function in store.
     await storePagination.changePageNumber(pageNumber, URLToBackend);
   }
};

</script>

<style scoped>

</style>

@/store/Pagination.js


import {defineStore} from 'pinia';
import {axios} from "@/common/api.axios";

export const usePaginationStore = defineStore('Pagination', {
  state: () => ({
    data: [],
    dataResult: [],
    dataLoading: false
  }),
  actions: {

    async changePageNumber(page, endpoint) {

      return this.getDataResult(endpoint + `?page=${page}`);
    },

    async getDataResult(endpoint) {

      this.dataLoading = true;
      try {
        this.data = await axios.get(endpoint);
        this.dataResult = this.data.data.results;
      }
      catch (error){
        console.log("Error while trying to retrieve the requested data from backend server!");
      }
      finally {
        this.dataLoading = false;
      }
    }
  },
});

Finally, I really appreciate your answers and suggestions (not too complex) to solve this manner.

0 Answers
Related