So ive been looking into vue and been experiencing an issue which i cant seem to find the solution for. Im using Vue and Vue-router. I started with the basic vue + webpack template which gave the initial boilerplate.
I've successfully added additional routes to the predefined routes which is working as expected (games, tournaments, stats and users routes works just fine). However now im unable to get additional routes to work. the "gaming" route doesnt work, ive also tried adding additional routes which does not seem to work either.
So this is my current router file (index.js):
import Vue from 'vue'
import Router from 'vue-router'
const Gaming = () => import('#/components/gaming.vue');
const Home = () => import('#/components/home.vue');
const Game = () => import('#/components/game.vue');
const Tournament = () => import('#/components/tournament.vue');
const Users = () => import('#/components/users.vue');
const Stats = () => import('#/components/stats.vue');
const router = new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/games',
name: 'Game',
component: Game,
},
{
path: '/wtf',
name: 'Gaming',
components: Gaming,
},
{
path: '/tournaments',
name: 'Tournament',
component: Tournament
},
{
path: '/users',
name: 'Users',
component: Users
},
{
path: '/stats',
name: 'Stats',
component: Stats
}
]
});
export default router;
Vue.use(Router);
All my routes works as expected except the "Gaming" route. The "Gaming" component looks like this:
<template>
<div>
<h1>WTF?!?!?!?!?=!?!</h1>
</div>
</template>
<script>
export default {
name: 'Gaming',
components: {},
data() {
return {}
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
</style>
Ive tried to pretty much copy/paste a working component, And only change the name, as well as the template. But it seems to have issues. Initially i had done the route component imports the normal "boilerplate" way
import Stats from '#/components/Stats'
Which pretty much had the same result, Except this would cause an exception when attempting to navigate to the "gaming" route.
Cannot read property '$createElement' of undefined
at render (eval at ./node_modules/vue-loader/lib/template-compiler/index.js?{"id":"data-v-c9036282","hasScoped":false,"transformToRequire":{"video":["src","poster"],"source":"src","img":"src","image":"xlink:href"},"buble":{"transforms":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/gaming.vue (app.js:4240), <anonymous>:3:16)
All other routes worked. Ive also tried to re-create all the files and re-do the specific route which doesnt seem to work either. So im at a loss of what i can do to fix this issue?
Here i attempt to inspect the route, And as you can see the component is missing "everything"
Inspecting the route
Also tried looking with the vue addon for chrome, Where the component does not get loaded into the view
Vue Chrome Extension
Uploaded the project to gdrive if someone want to tweak around with it
Google Drive Link
Found the issue:
const router = new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/games',
name: 'Game',
component: Game,
},
{
path: '/wtf',
name: 'Gaming',
components <----------: Gaming,
// Should be component not componentS
},
{
path: '/tournaments',
name: 'Tournament',
component: Tournament
},
...
Also, you should use the standard method of importing. Without that error, I would've never found the issue.
Related
I have the following structure of the code:
router.js:
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'App',
component: () => import('../../src/App.vue'),
},
{
path: '/:pathMatch(.*)',
name: 'ErrorView',
component: () => import('../components/Error.vue'),
}
],
});
export default router;
App.vue
<template>
<Land />
<LeftNavbar />
<Navbar />
<Experience />
<TechnologiesCarousel />
<Projects />
<Copyright />
<BackToTop />
</template>
When I'm pressing in the URL bar: http://localhost:3000. The app is rendering properly which is fine, but when I'm trying to write a wrong URL, for eg: http://localhost:3000/abcf/ || http://localhost:3000/dsafbdmhgfjweghjfw to be redirected to the 404 page, I'm not redirected, the page still rendering the App.vue component.
Does anyone have any idea why the 404 page isn't rendered?
Try like following /:pathMatch(.*)*:
{
path: "/:pathMatch(.*)*",
name: 'ErrorView',
component: () => import('../components/Error.vue'),
},
and in App.vue place <router-view /> and move components to other view.
I assume you are using Vue 3.
In your router file you need to change the path to this:
{
path: '/:pathMatch(.*)*',
name: 'ErrorView',
component: () => import('../components/Error.vue'),
}
For Vue 2 the path can be: path:'*'
From Vue 2 -> 3 migration some more info for those they want to know what has changed:
Essentially, you should be able to replace the '' path with '/:pathMatch(.)*' and be good to go!
Reason: Vue Router doesn't use path-to-regexp anymore, instead it implements its own parsing system that allows route ranking and enables dynamic routing. Since we usually add one single catch-all route per project, there is no big benefit in supporting a special syntax for *.
Using Vue 3, i have my router file set up this way
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/Portfolio",
name: "Portfolio",
component: () =>
import(/*webpackChunkName: "DestinationDetails" */ "../views/Portfolio"),
},
{
path: "/Services",
name: "Services",
component: () =>
import(/*webpackChunkName: "DestinationDetails" */ "../views/Services"),
},
{
path: "/details/:id",
name: "PortfolioDetails",
component: () =>
import(
/*webpackChunkName: "DestinationDetails" */ "../views/PortfolioDetails"
),
},
{
path: "/:pathMatch(.*)*",
redirect: "/Home",
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
I am also running a v-for loop to get paths from an API "https://api.fake.rest/ca2a6662-22d0-4010-ba08-0440ffe813ab/menu". 3 of the 5 url paths have a value of "#". the remaining two have normal paths.
<div
v-for="(men, index) in webMenu.menu_items"
:key="index" class=" mt-32"
>
<!-- <SidebarLink class="w-full" :to="{path:men.url}" icon="fas fa-home">{{
men.name
}}</SidebarLink> -->
<router-link class="w-full" :to="men.url"> {{men.name}} </router-link>
</div>
<p class="font-bold">{{webMenu.menu_text}}</p>
</div>
Problem is now when the webpage loads initially, it works fine but after clicking on the portfolio or services link, the paths to the others are changed.
e.g if i was on the portfolio page and tried switching back to the home page, it would change the route path to "portfolio#" and refuse to switch pages.
Can anyone help explain why this is and a possible way to resolve it?
I was also facing the similar issue.
For me the issue was that I was redirecting to new page using router.push() on click of a div.
And I was using #click.stop = "myFunction" . This was adding a # in the url and not redirecting the user.
I tried different variation of this as well. I tried using event object as well like event.stopPropagation(). With this as well got same result.
When I used #click.prevent, this everything worked as expected.
PS:
You can use catch block to trace the error as well.
this.$router..push({
name: "PAthName",
params: { id },
query: { id }
})
.catch(e => {
console.log("Errors", e);
});
Should have updated this earlier.
The fix was relatively simple, all i did was change the binding from
<router-link class="w-full" :to="men.url"> {{men.name}} </router-link>
to
<router-link class="w-full" :to="`/${men.url}`"> {{men.name}} </router-link>
and voila!
I have app where user can login in different roles, eg. seller, buyer and admin.
For each user I'd like to show dashboard page on the same path, eg. http://localhost:8080/dashboard
However, each user will have different dashboard defined in different vue components, eg. SellerDashboard, BuyerDashboard and AdminDashboard.
So basically, when user opens http://localhost:8080/dashboard vue app should load different component based on the user role (which I store in vuex). Similarly, I'd like to have this for other routes. For example, when user goes to profile page http://localhost:8080/profile app should show different profile component depending on the logged in user.
So I'd like to have the same route for all users roles as opposed to have different route for each user role, eg. I don't want user role to be contained in url like following: http://localhost:8080/admin/profile and http://localhost:8080/seller/profile etc...
How can I implement this scenario with vue router?
I tried using combination of children routes and per-route guard beforeEnter to resolve to a route based on user role. Here is a code sample of that:
in router.js:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import store from '#/store'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home,
beforeEnter: (to, from, next) => {
next({ name: store.state.userRole })
},
children: [
{
path: '',
name: 'admin',
component: () => import('#/components/Admin/AdminDashboard')
},
{
path: '',
name: 'seller',
component: () => import('#/components/Seller/SellerDashboard')
},
{
path: '',
name: 'buyer',
component: () => import('#/components/Buyer/BuyerDashboard')
}
]
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
in store.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userRole: 'seller' // can also be 'buyer' or 'admin'
}
})
App.vue contains parent router-view for top-level routes, eg. map / to Home component and /about to About component:
<template>
<router-view/>
</template>
<script>
export default {
name: 'App',
}
</script>
And Home.vue contains nested router-view for different user's role-based components:
<template>
<div class="home fill-height" style="background: #ddd;">
<h1>Home.vue</h1>
<!-- nested router-view where user specific component should be rendered -->
<router-view style="background: #eee" />
</div>
</template>
<script>
export default {
name: 'home'
}
</script>
But it doesn't work because I get Maximum call stack size exceeded exception in browser console when I call next({ name: store.state.userRole }) in beforeEnter. The exception is:
vue-router.esm.js?8c4f:2079 RangeError: Maximum call stack size exceeded
at VueRouter.match (vue-router.esm.js?8c4f:2689)
at HTML5History.transitionTo (vue-router.esm.js?8c4f:2033)
at HTML5History.push (vue-router.esm.js?8c4f:2365)
at eval (vue-router.esm.js?8c4f:2135)
at beforeEnter (index.js?a18c:41)
at iterator (vue-router.esm.js?8c4f:2120)
at step (vue-router.esm.js?8c4f:1846)
at runQueue (vue-router.esm.js?8c4f:1854)
at HTML5History.confirmTransition (vue-router.esm.js?8c4f:2147)
at HTML5History.transitionTo (vue-router.esm.js?8c4f:2034)
and thus nothing is rendered.
Is there a way I can solve this?
You might want to try something around this solution:
<template>
<component :is="compName">
</template>
data: () {
return {
role: 'seller' //insert role here - maybe on `created()` or wherever
}
},
components: {
seller: () => import('/components/seller'),
admin: () => import('/components/admin'),
buyer: () => import('/components/buyer'),
}
Or if you prefer maybe a bit more neat (same result) :
<template>
<component :is="loadComp">
</template>
data: () => ({compName: 'seller'}),
computed: {
loadComp () {
const compName = this.compName
return () => import(`/components/${compName}`)
}
}
This will give you the use of dynamic components without having to import all of the cmps up front, but using only the one needed every time.
Such code retrieves component code only for a given role:
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
import store from "../store";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "home",
component: () => {
switch (store.state.userRole) {
case "admin":
return import("../components/AdminDashboard");
case "buyer":
return import("../components/BuyerDashboard");
case "seller":
return import("../components/SellerDashboard");
default:
return Home;
}
}
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;
One approach would be to use a dynamic component. You could have a single child route whose component is also non-specific (e.g. DashboardComponent):
router.js
const routes = [
{
path: '/',
name: 'home',
children: [
{
path: '',
name: 'dashboard',
component: () => import('#/components/Dashboard')
}
]
}
]
components/Dashboard.vue
<template>
<!-- wherever your component goes in the layout -->
<component :is="dashboardComponent"></component>
</template>
<script>
import AdminDashboard from '#/components/Admin/AdminDashboard'
import SellerDashboard from '#/components/Seller/SellerDashboard'
import BuyerDashboard from '#/components/Buyer/BuyerDashboard'
const RoleDashboardMapping = {
admin: AdminDashboard,
seller: SellerDashboard,
buyer: BuyerDashboard
}
export default {
data () {
return {
dashboardComponent: RoleDashboardMapping[this.$store.state.userRole]
}
}
}
</script>
You run into the Maximum call stack size exceeded exception because the next({ name: store.state.userRole }) will trigger another redirection and call the beforeEnter again and thus results in infinite loop.
To solve this, you can check on the to param, and if it is already set, you can call next() to confirm the navigation, and it will not cause re-direction. See code below:
beforeEnter: (to, from, next) => {
// Helper to inspect the params.
console.log("to", to, "from", from)
// this is just an example, in your case, you may need
// to verify the value of `to.name` is not 'home' etc.
if (to.name) {
next();
} else {
next({ name: store.state.userRole })
}
},
I faced the same problem (I use Meteor JS with Vue JS) and I found the way to do it with the render function to load different components on the same route. So, in your case it should be:
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
import AdminDashboard from "../components/AdminDashboard";
import BuyerDashboard from "../components/BuyerDashboard";
import SellerDashboard from "../components/SellerDashboard";
import store from "../store";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "home",
component: {
render: (h) => {
switch (store.state.userRole) {
case "admin":
return h(AdminDashboard);
case "buyer":
return h(BuyerDashboard);
case "seller":
return h(SellerDashboard);
default:
return h(Home);
}
}
}
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;
Note that this solution also works but only for the first time, if you enter again to that route, the last component loaded it will keep (you will need to reload the page). So, with the render function it always load the new component.
Vue Router 4 (Vue 3)
If you are using Vue Router 4 (usable only with Vue 3), one alternative solution is to use dynamic routing
This new feature allows us to remove/add routes on the fly.
// router.js
import { createRouter, createWebHistory } from 'vue-router'
import store from "../store";
import Home from "../views/Home.vue";
import About from "../views/About.vue";
// all routes independent of user role
const staticRoutes = [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/about",
name: "about",
component: About,
},
]
const getRoutesForRole = (role) => {
// imlementation can vary - see the rest of the answer
}
// routes used at app initialization
const initialRoutes = [...staticRoutes, ...getRoutesForRole(store.state.userRole)]
const router = createRouter({
history: createWebHistory(),
routes: initialRoutes,
})
export default router
export const updateRoutesForRole = () {
// implementation can vary - see the rest of the answer
}
How to generate dynamic routes - getRoutesForRole
The implementation of course depends on many factors - how many routes (and also roles) do you have is probably most important.
With just 2 or 3 routes (and not many roles) it is just fine to use a static definition:
const routesPerRole = {
"admin": [
{
path: "/dashboard",
name: "dashboard",
component: () => import("../components/AdminDashboard.vue")
}, // more routes follow....
],
"seller": [
{
path: "/dashboard",
name: "dashboard",
component: () => import("../components/SellerDashboard.vue")
}, // more routes follow....
],
"buyer": [
{
path: "/dashboard",
name: "dashboard",
component: () => import("../components/BuyerDashboard.vue")
}, // more routes follow....
],
}
const getRoutesForRole = (role) => {
if(!role) return []
return routesPerRole[role]
}
If you have many routes and/or many roles, you probably want something more generic. First we need some good naming convention - for example lets say that we will organize our components in a directory structure like this: #/components/${role}/${componentName}.vue
Then we can use Webpacks dynamic import
const routeTemplates = [
{
path: "/dashboard",
name: "dashboard",
component: 'Dashboard'
},
]
const getRoutesForRole = (role) => {
if(!role) return []
const routesForRole = routeTemplates.map(route => ({
...route,
component: () => import(`#/components/${role}/${route.component}.vue`)
}))
return routesForRole
}
Note that thanks to how import() with dynamic expression works in Webpack this will make Webpack to create new JS chunk for each component in #/components folder which may be not what you want.
Easy fix is to move the "role dependent" components into it's own subfolder so instead of using #/components/admin/.... just use #/components/perRoleComponents/admin/.... and
import(`#/components/perRoleComponents/${role}/${route.component}.vue`)
Other solution is to use different import() statement for each role. This will also allow us to use Webpacks "magic comments" and for example force Webpack to pack all components for each role into single js chunk:
const routeTemplates = [
{
path: "/dashboard",
name: "dashboard",
component: 'Dashboard'
},
]
const getComponentLoader = (role, componentName) => {
switch(role) {
"admin": return () => import(
/* webpackChunkName: "admin-components" */
/* webpackMode: "lazy-once" */
`#/components/admin/${componentName}.vue`)
"seller": return () => import(
/* webpackChunkName: "seller-components" */
/* webpackMode: "lazy-once" */
`#/components/seller/${componentName}.vue`)
"buyer": return () => import(
/* webpackChunkName: "buyer-components" */
/* webpackMode: "lazy-once" */
`#/components/buyer/${componentName}.vue`)
}
}
const getRoutesForRole = (role) => {
if(!role) return []
const routesForRole = routeTemplates.map(route => ({
...route,
component: getComponentLoader(role, route.component)
}))
return routesForRole
}
How to update routes - updateRoutesForRole()
Easiest scenario is when each role has same set of routes and just wants to use a different component. In this case to switch the routes when role changes we can just use addRoute
Add a new route record to the router. If the route has a name and there is already an existing one with the same one, it removes it first.
export const updateRoutesForRole = () {
const role = store.state.userRole
const routesForRole = getRoutesForRole(role)
routesForRole.forEach(r => router.addRoute(r))
}
For more complicated scenarios where not all routes are available for all roles, previous routes (for previous active role - if any) must be removed 1st using removeRoute function. Also our getRoutesForRole() must be different. One solution is to use route meta fields
const routeTemplates = [
{
path: "/dashboard",
name: "dashboard",
component: 'Dashboard',
meta: { forRoles: ['admin', 'seller'] }
},
]
const getRoutesForRole = (role) => {
if(!role) return []
const routesForRole = routeTemplates
.filter(route => route.meta?.forRoles?.includes(role))
.map(route => ({
...route,
component: () => import(`#/components/${role}/${route.component}.vue`)
}))
return routesForRole
}
export const updateRoutesForRole = () {
const role = store.state.userRole
// delete previous 1st
router.getRoutes()
.filter(route => route.meta?.forRoles)
.forEach(route => router.removeRoute(route.name))
const routesForRole = getRoutesForRole(role)
routesForRole.forEach(r => router.addRoute(r))
}
Router v3 (for Vue 2)
Note that Router v3 (and earlier) was never designed with dynamic routing in mind. There is no removeRoute() function. There is a addRoute() so some of the scenarios described above could be probably possible but it currently (Router v3.5.3) does not work as described in the documentation
One way to solve this is to create three separate components DashboardForAdmin, DashBoardForSeller, and DashBoardForBuyer for three types of users.
Then use a mixin.js
export default {
data: function () {
return {
userType : "buyer"; // replace this with a function that returns "seller", "buyer", or "admin"
}
}
}
Create a Vue component DashboardContainer renders the correct dashboard component based on mixin return value
<template>
<div>
<div v-if="userType === 'admin'">
<DashboardForAdmin />
</div>
<div v-else-if="userType === 'buyer'">
<DashboardForBuyer />
</div>
<div v-else>
<DashboardForSeller />
</div>
</div>
</template>
<script>
import mixin from '#/mixin.js';
import DashboardForAdmin from '#/components/DashboardForAdmin.vue';
import DashBoardForSeller from '#/components/DashBoardForSeller.vue';
import DashBoardForBuyer from '#/components/DashBoardForBuyer.vue';
export default {
mixins: [mixin],
components: {
DashboardForAdmin, DashBoardForSeller, DashBoardForBuyer
},
};
</script>
Now you can add a single route for the DashboardContainer
I want to have a couple of "overview" pages for sections my app, that will all be triggered on the root of that section.
so localhost/hi should display component HiOverview
localhost/he should display component HeOverview
as there are multiple of those, i want to avoid assigning the component to a const, then reusing it in a route. instead i want to handle all that in a single dynamic route.
BUT i'm struggling with the creation of the Components in the beforeEnter hook.
each route object expects a component... but i just want to decide the component depending on route. (sectionsWithOverview is a simple array of strings containing the names of routes where i want an overview displayed
const router = new Router({
linkActiveClass: 'active',
mode: 'history',
routes: [
{ path: '/:section',
component: Placeholder,
beforeEnter: (to, from, next) => {
const section = to.params.section
// how can i use this in the next() call?
// const View = () => import(/* webpackChunkName: 'sectionView' */ `Components/${section}/${section}Overview`)
if (sectionsWithOverview.includes(to.params.section)) {
next({ name: `${to.params.section}Overview` })
} else {
next()
}
},
}
can you guys help me? how can i conditionally assign a component onBeforeEnter, and then route to that exact Component?
it works if i declare each SectionOverview beforehand, but that makes my whole idea pointless.
Thanks for any help :)
I made something similar with a project but instead I used beforeRouteUpdate
Here is an example of how it works. On route.js simply define your dynamic route
const router = new Router({
linkActiveClass: 'active',
mode: 'history',
routes: [
{
path: '/:section',
component: Placeholder,
name: 'placeholder'
},
}
Then in your component (I assume Placeholder.vue) in your HTML add this line of code
<transition name="fade" mode="out-in">
<component :is="section" key="section"></component>
</transition>
then in your JS add the beforeRouteUpdate hook and define all your components that will match your route section param.
import he from './heOverview.vue'
import hi from './hiOverview.vue'
beforeRouteUpdate (to, from, next) {
// just use `this`
this.section = to.params.section
next()
},
components: {
he,
hi
},
data () {
return {
section: ''
}
}
So when a user navigate to localhost/he the heOverview.vue component will be loaded. The only thing you have to make sure is that the section param's value should match an specific view if not an error will be produced
If you need more info about how this work, read
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards
I have implemented localisation with vue-i18n.
my main.js
import Vue from 'vue'
import { i18n } from './plugins/i18n'
import Cookie from "vue-cookie";
if (!Cookie.get('locale')) {
Cookie.set('locale', 'en', 1)
}
new Vue({
el: '#app',
router,
i18n,
template: '<App/>',
components: {App},
render: h => h(App),
mounted() {},
data: {
event: false
}
}).$mount();
my i18n.js plugin
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import enTranslations from '../../lang/en'
import slTranslations from '../../lang/sl'
import Cookie from 'vue-cookie'
Vue.use(VueI18n);
export const i18n = new VueI18n({
locale: Cookie.get('locale'),
fallbackLocale: 'en', // fallback option
messages: { en: enTranslations, sl: slTranslations}
});
My routes
{
path: '/:lang',
component: {
template: '<router-view></router-view>'
},
children: [
{
path: '',
name: 'Home',
component: Home
},
{
path: 'contact',
name: 'Contact',
component: Contact
}
]
}
And my switch language function in my navigation component
setLocale(locale) {
let selectedLang = locale.toLowerCase();
Cookie.set('locale', selectedLang, 1);
this.$router.push({name: this.$route.name, params: {lang: selectedLang}});
location.reload();
},
So far everything ok and working when I switch language via upper function setLocale(). The problem appears when user inputs url directly for example:
I have currently selected english language and then user visits page directly via url, let's say: localhost:8080/sl/contact
If I understand documentation correctly I should configure this in routes with beforeEnter function. So my current implementation looks like this.
beforeEnter: (to, from, next) => {
let selectedLang = to.params.lang.toLowerCase();
Cookie.set('locale', selectedLang, 1);
next();
},
But this doesn't do the trick, because it's only working on second reload.
So the cooke locale is set to correct language, but looks like them component code happens before so UI is still in old language. When I refresh again, then content of page is in correct language. How can I overcome this problem?
If you need any additional information's please let me know and I will provide. Thank you!
When you navigate from localhost:8080/sl/contact to localhost:8080/en/contact, the same **'Contact'**vue component instance will be reused. Since both routes render the same component,
Please refer to the documentation:
https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes.
To re-render the contact component you could either watch the $route object or use in-component navigation guards beforeRouteUpdate to react to changes and then reload your component or any application logic you wish to execute.
To know further about in-component navigation guards please refer to this link https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard
Please try this,
Option 1:
watch:{
$route(to, from){
let selectedLang = to.params.lang.toLowerCase();
Cookie.set('locale', selectedLang, 1);
//reload your component
}
Option 2:
beforeRouteUpdate: (to, from, next) => {
let selectedLang = to.params.lang.toLowerCase();
Cookie.set('locale', selectedLang, 1);
next();
},