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.