I encountered a curious case during development.
I have a modal component that warns users about a change in price:
If a user clicks the button I want the page to reload and automatically run the onMounted hook, after that calculate a new price inside the hook with an apiCall.
The behavior I encountered is that nothing changes about the page and the modal remains open like it's ignoring the router.push() method.
I wanted to implement this solution in Vue 3 and understand why router.push() doesn't work. I found a workaround with window.location.href but I'd love to hear the reason why using the standard router is not allowing me to succed with the standard vue-router.
I am inside the route /payment?code=123&hash=123, the object routerEnums.PAYMENT is equal to '/payment', It works fine with any other route except this one.
/**script**/
import {useRouter} from "vue-router";
const router = useRouter();
/**template**/
<change-price-modal-good
v-if="showModalPriceChange"
@price-change-button-clicked="router.push(routerEnums.PAYMENT)"
/>
history.pushState
or history.replaceState
(with more caution)If you modify the history natively, Vue Router won't be notified, and it won't issue the appropriate instructions for DOM updates. If you use history.pushState
, a new entry will be added to the history; if you use history.replaceState
, only the browser's header will be updated.
const removeQueryParamsWithoutReload = () => {
const { href } = this.$router.resolve(route);
// pushState: without reload, adds a new entry to the history
// replaceState: without reload, updates the history without adding a new entry
history.replaceState(
{},
null,
href, // Without query object
);
};
const removeSpecificQueryParamWithoutReload = (paramToRemove) => {
const { href, route } = this.$router.resolve(route);
const currentQuery = { ...route.query };
delete currentQuery[paramToRemove]; // Delete a specific parameter
const newQuery = Object.keys(currentQuery).length
? '?' + new URLSearchParams(currentQuery).toString()
: '';
// pushState: without reload, adds a new entry to the history
// replaceState: without reload, updates the history without adding a new entry
history.replaceState(
{},
null,
href + newQuery,
);
};
IMPORTANT NOTE: Vue Router is not notified of the URL change, so the information it provides will remain outdated until the next page reload. From the questioner's perspective, this isn't necessarily bad, as the query parameters will disappear, and everything else should remain intact. However, regardless of this, you can no longer rely on the data from Vue Router.
router.replace
It acts like
router.push
, the only difference is that it navigates without pushing a new history entry, as its name suggests - it replaces the current entry.Declarative
<router-link :to="..." replace>
Programmatic
router.replace(...)
It's also possible to directly add a property
replace: true
to the to argument that is passed torouter.push
:router.push({ path: '/home', replace: true }) // equivalent to router.replace({ path: '/home' })
With the following functions, you will be able to remove the entire queryParams
or a part of it without reloading the entire page:
const removeQueryParams = () => {
router.replace({
path: router.currentRoute.value.path,
query: {}, // Empty query object
});
};
const removeSpecificQueryParam = () => {
const currentQuery = { ...router.currentRoute.value.query };
delete currentQuery.someParam; // Delete a specific parameter
router.replace({
path: router.currentRoute.value.path,
query: currentQuery,
});
};
force: boolean
(will be deprecated?)There used to be an undocumented force boolean variable that allowed you to enforce the desired behavior. However, based on reports, it is either planned for deprecation or has already been removed.
force?
property - Vue Router API Docs