Disclaimer:
Using `Vue 2` with `@vue/composition-api`
Ever wandered how to make a custom back button?
It might seem trivial, right?
Just `router.go(-1);`
But what if you want to act it more like a breadcrumb, where the back button goes from the details view back to the listing page?
Seems simple enough:
router.push({name:'SomeList'})
Ok but what if that list has query/search/pagination?
Well for that you will need to start tracking some information in one place or other.
There is a plugin for vue:
Vuex Router Sync
But if you're not using Vuex or you try to avoid depencies where possible, there's a simple solution that i've come accross.
The solution:
Sync your query to the current route
first we need to sync query params to vue router:
import { removeNullProperties } from './useUtilities.js';
import { useRouter } from './useRouter.js';
// composable function
export const syncQuery = (query) => {
// injected vue router instance
const router = useRouter();
const updateQueryParams = () => {
const url = new URL(window.location);
// reseting the search as window.location includes the current query params as well
url.search = '';
// removing any null/empty properties so that the url stays a clean as possible
Object.keys(removeNullProperties(query)).forEach((key) => {
url.searchParams.set(key, query[key]);
});
const { pathname, search } = url;
const newPath = `${pathname}${search}`;
const currentPath = router.currentRoute.fullPath;
// if the path changed let's update vue router
if (newPath !== currentPath) {
router.push(newPath);
}
};
// watched to watch query for changes
watch(
() => query,
() => {
updateQueryParams();
},
{ deep: true }
);
};
Use vue router global resolve guard
Now we need to store some meta information, you have two simple options when using vue router Global resolve guards beforeResolve
or beforeEach
in this example i'll use beforeResolve
router.beforeResolve = (to, from, next) => {
if (!to.meta) {
to.meta = {};
}
to.meta.from = {
name: from.name,
params: from.params,
query: from.query,
};
}
This adds all we need to navigate back to the from route.
A composable to get you a true path back
Next i've wrote another composable function to use on the custom back button:
import { useRouter } from './useRouter.js';
export const useRouteBack = (route) => {
const router = useRouter();
const from = router.currentRoute.meta?.from;
if (from && from.name === route.name) {
return from;
}
return route;
};
which you can use simply like so:
<router-link :to="useRouteBack({name: 'SomeList'})>
back to list
</router-link>
That's it. I've spent quite some time gathering all the bits and bobs, but this solution seemed like the one involving the least amount of effort/code.
Initializing list page from the query
This will really depend on your solution, but this is the most important part to make things work.
In essence you need to make sure your list page can initialize while using the router.query
params.
What i've done is add router query handling to the List pages so that my first calls to the api match the initial params provided from the url.
This is a very custom solution for each page.
Since you could have a page with complex filters which v-model your query with Objects and your url/api only needs the simple data structures like
id: number|string
date: string
name: string
I'm still in the process of updating all the list pages.
Maybe if i find some common ground which i could refine into a nice function/pattern i'll update this post.
Hopes this helps, and Good Luck with your coding adventures!
Top comments (0)