Access route params within route/ subRoutes object in Vuejs - vue.js

Can anybody tell me if there is a way to access the route params directly inside the route objects?, I need to require different components based on the route object.
router.map({
'/dashboard/*': {
component: Home
},
'/dashboard/:module': {
component: Module,
subRoutes: {
'/': {
component: require('./components/'+this.$route.params.module+'.vue')
},
'/:tab': {
component: require('./components/'+this.$route.params.tab+'.vue')
}
}
}
});

You can't require one or another component depending on the route parameters directly in the router configuration. What you can do instead is create a wrapper component that will use a component or another depending on the route parameters, as the route parameters are accessible from any components.
As written in the vue-router documentation:
The route object will be injected into every component in a vue-router-enabled app as this.$route, and will be updated whenever a route transition is performed.
Every of your components have access to the current route through this.$route therefore you could do something like this:
<template>
<component-a v-if="$route.params.param1 === 'a'></component-a>
<component-b v-if="$route.params.param1 === 'b'></component-b>
</template>

I haven't seen the use of $route.params inside the route objects. What you can do though, in your vue you can dynamically load different components based on your $route.params
<component-a v-if="$route.params == 'a'"></component-a>

Related

What is RouterView in Vue.js?

When I do the Vue quickstart and it creates a HelloWorld application, I see that App.vue line 20 contains this line:
<RouterView />
I can't find any documentation for RouterView. It seems odd that a quickstart / tutorial would include an undocumented tag. I do see router-view, is that the same thing?
The RouterView or router-view component is used to display the component or template that corresponds to the current route.
Source: vue-router.d.ts
/**
* Component to display the current route the user is at.
*/
export declare const RouterView: new () => {
$props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterViewProps;
$slots: {
default: (arg: {
Component: VNode;
route: RouteLocationNormalizedLoaded;
}) => VNode[];
};
};
Here is a webpage detailing how to use the RouterView component: How to Use Vue Router's router-view Component
After doing some of my own experimenting, I created a vue app (with Vue Router), I found that my components were not being displayed even if I was at the specified route. After adding the RouterView component to my App.vue template, I was able to see the view that was specified by the route.
Edit:
I'd suggest looking through the following tutorial:
Create your own Vue.js Router. After I created my own router I can now understand why this component is present. The component will render the component that the route specifies, therefore, when the route changes the component rendered will change.
From Vue docs for Component Registration > Name Casing:
When defining a component with PascalCase, you can use either case when referencing its custom element. That means both <my-component-name> and <MyComponentName> are acceptable. Note, however, that only kebab-case names are valid directly in the DOM (i.e. non-string templates).
So <RouterView /> is the same as <router-view />.

Is there any way to have dynamic routes/components in vuejs router?

Hi beautiful Vuejs developers out there!
I have a little problem with routing many Vue components/pages dynamically. In this scenario I am using nested routes to have a couple of routes for my layout components and hundreds of child routes for my pages and as you can imagine I'll have to type many child routes statically or manually, and then add more when I need more child routes in the future code changes but I need a solution to simplify/solve this problem with more efficient/better way like adding those routes from what user types after the layout in the url... here is my example code code:
const routes: RouteRecordRaw[] = [
{
{
path: '/student',
component: () => import('layouts/StudentLayout.vue'),
children: [
{
path: 'dashboard',
component: () => import('pages/student/Dashboard.vue'),
},
{
path: 'profile',
component: () => import('pages/student/Profile.vue'),
},
],
},
}
As you see in this code I have a layout named Student and it has two children but I'll have to type manually hundreds of child routes for this layout and other layouts is there any way to dynamically set up those routes with what users enter after the layout name like /student/dashboard or /layout/page and match it with a component name? I mean like params in Angular, can I use the param value itself inside the router to say?
{
path: ':pagename',
component: (pagename) => import('pages/student/' + pagename + '.vue'),
},
let me know if there is an efficient way to solve this problem.
Thanks in advance!
I would, personally, not use this, or advise such an approach, nor have I done it, but this idea came to me when I read your question:
My best guess would be to have a handler component which renders a component dynamically based on a route parameter (say you have /:subpage as a child to /student and the handler component is set to that route), and an exception handler around that to show a 404 page when the user types in an inexistent/unsupported route.
For example, I would dynamically import the component by the route parameter into a predefined let (e.g. let SubpageComponent; outside the try catch block), have a try catch block around the dynamic import assignment for the respective error where catch would set the variable to a 404 page. Then I would add the SubpageComponent into the data() of the component doing the rendering of the route.
Edit
I've written out come code that, maybe, makes sense.
It's based on https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components
your routes definition, changed
const routes: RouteRecordRaw[] = [
{
path: '/student',
component: () => import('layouts/StudentLayout.vue'),
children: [
{
path: '/:subpage',
component: () => import('pages/student/SubpageRenderer.vue'),
props: true,
},
],
},
]
SubpageRenderer.vue
<script>
export default {
props: ['subpage'],
data() {
return {
currentSubpage: () => import(`./${subpage}.vue`)
}
}
}
</script>
<template>
<component :is="currentSubpage"></component>
</template>
Instead of using the currentSubpage import, you can also use the subpage route prop to bind :is if subpage is the name of a registered component.
Since this would get only "dashboard" from the route, you'd need some namespacing, like "student-dashboard" with the help of template literals. You could make currentSubpage into a template literal that creates the student-${subpage} name.
I'd probably recommend importing the options object of the component designated by the subpage route parameter instead of registering all the components - if you're registering them, you might as well use vue-router the usual way :)
Also, I only think this could work! It should be tested out, and perhaps casing should be kept in mind, and maybe the Layout suffix as well (subpage will probably be all lowercase, and you'll probably have the components named in PascalCase). After uppercasing the first letter, this could also obviously lead to both /student/Dashboard and /student/dashboard being valid routes

How can I refresh a route every time it is accessed in Vue Router 4?

I have setup a route in vue-router 4 that should load a component dynamically depending on whether the user is logged in or not. I did it like this (there may be a better way?):
import Personal from '../views/Personal.vue';
import Public from '../views/Public.vue';
routes: [
{
path: '/',
component: async () => {
const isLoggedIn = await authenticateUser();
if (isLoggedIn == true) {
return Personal
} else {
return Public
}
}
}
]
The App.vue file is this:
<template>
<div id="app">
<Site-Header></Site-Header>
<router-view></router-view>
<Site-Footer></Site-Footer>
</div>
</template>
The problem is that if a user logs in from the homepage route with path of '/', he doesn't navigate away from this route. Instead I would like vue-router to just load the Personal component instead.
The switch between Personal and Public only seems to work if I hard refresh the page, otherwise no changes happen. So if a user logs in, they still see the Public.vue component, then after a page refresh they see the Personal.vue component. If they then logout, they still see the Personal.vue component until they refresh the page.
How could I force vue-router to analyse the route after log-in/log-out and load the correct component?
To have multiple routes utilizing the same path, your best bet is using Named Views. You can define the default component for your index, or / path to be your Public component, while conditionally selecting a different component using v-if in your template.
You could define your routes as:
routes: [
{
components: {
default: Public,
Personal: Personal
},
name: "index",
path: "/"
}
]
Important to note that the syntax here differs. The component field here has to be pluralized in order for this to work, so it has to be components.
Then in your root template that's calling the views, you can then use a simple v-if to switch between them, depending on whether the user is logged in or not. How you store that information is up to you, so just adapt the below code to reflect your own app's logic
<!-- The default is Public, so you don't have to provide the name here -->
<router-view v-if="!user.loggedIn" />
<router-view v-else name="Personal" />
You can see this in this codesandbox

Reset vue component context on load when different routes use same component

Using vue router, I have two routes that are mapped to the same component, and given one route i am passing in a prop as a differentiator, then conditionally rendering content based on that prop. like so.
export default {
path: '/advanced-search',
name: 'Advanced Search',
component: AdvancedSearch
}
and
export default {
path: '/create-vendor-search',
name: 'Create Vendor Search',
component: AdvancedSearch,
props: { createMode: true }
}
These exist in two different files, advanced-search.route.js and createNewVendorSearh.route.js, respectively. The problem is that both pages are linked in my nav bar, and when i go directly from one to the other, the local state and vuex state of the components don't reset. I need this to be a brand new instance of this component every time a route calls it. How can I achieve this?

Dynamically add child routes in Vuejs

According to this official example, we have the ability to add nested/children routes in vuejs. But I cannot find any help/docs around a way to add these children routes dynamically. e.g only add child routes when Parent route is visited.
Currently all the routes in a Vue application are defined in a single place where we create Router instance. There is an api called addRoutes, but I don't know how to use that to add lazily loaded features of application along side their routes. If someone is familiar with Angular2+ Module system, that has this ability to define routes for the feature modules inside that module and even make them lazily loaded. Wondering if something could be achieved with VueJs?
You can use $router.addRoutes to re-add a route, specifying children.
You'll need to get the current route definition (as opposed to the $route object) by searching the $router.options.routes array for the route definition object that matches the current $route.path. Then add a children array of route definitions to the object and pass it to $router.addRoutes.
created() {
let { routes } = this.$router.options;
let routeData = routes.find(r => r.path === this.$route.path);
routeData.children = [
{ path: 'bar', component: Bar },
{ path: 'baz', component: Baz },
];
this.$router.addRoutes([routeData])
}
Here's an example fiddle of dynamically adding child routes in the created hook of a route's component definition.