Reset vue component context on load when different routes use same component - vue.js

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?

Related

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

Where did I go wrong with vue-router configuration, or the problem is somewhere in my component?

I have list of users which I output in Home vue component. Every item in the list is coming from vuex and has it's own details. When I click any of this contacts list items vue-router takes me to route /contact/that-item-id for example contact/4536475. Now, when I am on that page for specific contact list item and refresh my browser vue app breaks, in other words I don't have access to that specific item object properties anymore.
Here is the code of my router
export default new Router({
routes: [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/contact/:id",
name: "ContactDetails",
props: true,
component: ContactDetails
I am setting props property to true so I can pass it as params to contact item details component as so:
<router-link
class="view-more-btn"
:to="{ name: 'ContactDetails', params: { id: contact.id }}"
>VIEW DETAILS</router-link>
and at last I am passing that Id to my getters method in vuex to get details for clicked item as this:
export default {
props: ["id"],
computed: {
contact() {
return this.$store.getters.getContactDetails(this.id);
}
}
Where did I go wrong, why I can't refresh my contact item detail page and still preserve state I am using.
I am new to vue so please forgive me if I am not making sence. And ofcourse any help is welcomed, thanks in advance
The problem is probably, that you're referencing a named route and passing in the params by hand. This won't change the actual route displayed in your browsers address bar and only show the root path (/contact/ in your example I presume). Therefore when you refresh the passed in params/props simply don't exist anymore.
What you need to do instead is use a <router-link :to="'/contact/'+contact.id"> or <router-link :to="`/contact/${contact.id}`"">.
This should affect the URL in your browsers address bar to include the /contact/someID123 which will then also make the ID available on refresh.

How to Properly Use Vue Router beforeRouteEnter or Watch to trigger method in Single File Component?

I'm working on an app in Vue.js using Single File Components and Vue Router. I have a Search component where I need to execute a method to re-populate search results each time a user visits the route. The method executes correctly the first time the route is visited because of the "create" hook:
created: function() {
this.initializeSearch();
},
However, when the user leaves the route (to register or log into the app for instance), and returns to the Search page, I can't seem to find a way to automatically trigger this.initializeSearch() on subsequent visits.
Routes are set up in index.js like so:
import Search from './components/Search.vue';
import Login from './components/Login.vue';
import Register from './components/Register.vue';
// Vue Router Setup
Vue.use(VueRouter)
const routes = [
{ path: '/', component: Search },
{ path: '/register', component: Register },
{ path: '/login', component: Login },
{ path: '*', redirect: '/' }
]
export const router = new VueRouter({
routes
})
I gather that I should be using "watch" or "beforeRouteEnter" but I can't seem to get either to work.
I tried using "watch" like so within my Search component:
watch: {
// Call the method again if the route changes
'$route': 'initializeSearch'
}
And I can't seem to find any documentation explaining how to properly use the beforeRouteEnter callback with a single file component (the vue-router documentation isn't very clear).
Any help on this would be much appreciated.
Since you want to re-populate search results each time a user visits the route.
You can use beforeRouteEnter() in your component as below:
beforeRouteEnter (to, from, next) {
next(vm => {
// access to component's instance using `vm` .
// this is done because this navigation guard is called before the component is created.
// clear your previously populated search results.
// re-populate search results
vm.initializeSearch();
})
}
You can read more about navigation guards here
Here is the working fiddle

Declarative router link not clearing model in Vue component using VueRouter

Developing a task scheduler the path '/task?id=10' fills the component model with an async response with the task information, that works ok.
In the navigation bar I have the following router link:
<router-link to="/task">New Task</router-link>
So I am using the same path and component for creating a new task and editing existing ones all based in if "id" parameter is present or not.
The problem is that if I am in path '/task?id=10' and I fill some model fields, then I click the router-link pointing to '/task' (no params) it changes the browser URL but it does not clear the component model so the input data persists.
How can I restart/reload the component when landing it through a declarative router-link?
You can make parent-child component. Parent component is on /task route, child would be on '/task/10. More, Nested Routes. Also, you don't need to append '?id=', just /task/10.
const router = new VueRouter({
routes: [
{ path: '/task', component: Task,
children: [
{ path: ':id', component: TaskId }
]
}]
});
jsfiddle

Access route params within route/ subRoutes object in Vuejs

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>