i have 4 pages
list home [ Home.vue ]
create home [ CreateOrView.vue ]
view home [ CreateOrView.vue ]
summary [ Summary.vue ]
from page list home i have 2 button
first button
:to="{
name: 'view-home',
params: {
id: props.row.detail_id,
api: 'view_home'
}
}"'
second button
:to="{
name: 'summary',
params: {
id: props.row.detail_id,
api: 'view_home'
}
}"'
then on page view home
:to="{
name: 'summary',
params: {
id: props.row.detail_id,
api: 'view_home'
}
}"'
the last one page summary
i have back button with router back or go(n) like these https://stackoverflow.com/a/48123669/8122500
unExpected : list home --> view home --> summary --> go back --> create home
expected : list home --> view home --> summary --> go back --> view home
expected : list home --> summary --> go back --> list home
then my problem is when i go back ( from summary ) its clear params on previous. so when i go back the view is create home , my expected is view home.
i looking for best tricks ...
i have code
hasHistory() {
return window.history.length > 3
},
but in other case i have much page (more than 4 pages ) so its not solve .
router.go/router.back are very close to window.history.go APIs which need strict debugging if it comes to history manipulation and we manage them by using tricks like hardcode the numbers, router.go(1), router.go(2), etc.
If you are looking for a better approach for router navigation (forward and backward), write the navigation process inside the route's meta properties.
For example, if you want to navigate back from Summary.vue page to ViewHome.vue page, then you can write this logic inside your summary route like this-
{
path: "/summary",
name: "Summary",
component: "SummaryComponent"
meta: {
backRoute: "ViewHome"
},
},
And your back button inside Summary.vue would look like this-
<button #click=$router.push({ name: $route.meta.backRoute })>Go Back</button>
Using this approach you will always be sure that back button from Summary.vue page will always redirect to ViewHome.vue page.
The same meta-property concept can be used for all routes you want to navigate forward and backward.
This part of the code the person in the other question put to check that the user would "go back" to their application and not a 3rd party's website. If you want to keep that functionality I suggest you keep that as:
hasHistory() {
// this can stay at 2 and your user will only navigate "back" if the last route was part of your app
return window.history.length > 2;
}
To go back one page you can do $router.go(-1) or $router.back() like so: <button #click="$router.go(-1)">back</button>.
If you need to add the params you can use beforeRouteEnter navigation guard and use router.push() in your back button. Here's some boilerplate:
// Summary.vue
<template>
<button #click="$router.push(previousRoute)>Back</button>
</template>
<script>
beforeRouteEnter (to, from, next) {
next(vm => {
vm.previousRoute = from;
});
},
data () {
return {
previousRoute: undefined
}
}
</script>
Related
I am using Vue JS 3 and Vue Router. I have a company area of the app that uses a dynamic companyId parameter in the route. Ex. myapp.com/46/tasks where 46 is the companyId. Everything works fine when I navigate around to the different sub areas of the company area. However, if I am displaying a router link on any page, and that router link depends on the companyId parameter, if I try to navigate anywhere outside of the company area, which does not require the companyId, the reactivity of the router-link throws an error and the navigation does not happen. If I'm located at the route referenced above, and I try to navigate to
<router-link v-if="session.availableAccounts.length > 1" :to="{name: 'selectCompany'}">
{{ session.selectedAccount.name }}
</router-link>
Here is the router-link that throws the error: (however this happens on any page, with any router-link that requires parameters from the existing page and I then try to navigate somewhere without passing in the parameters EVEN THOUGH THE PARAMETER IS NOT NEEDED FOR THE ROUTE I AM TRYING TO GO TO)
<router-link
:to="{
name:'users',
query: {
selected: person.id,
area: 'Info'
}
}">
{{ person.name }}
</router-link>
Here is the portion of my router.js file concerning the 2 routes I am trying to move between.
{
path: '/account',
component: Base,
meta: {
authorization: true
},
children: [
{
name: 'newAccount',
path: 'new',
component: NewAccount,
meta: {
authorization: true,
title: 'New Account'
}
},
{
name: 'selectCompany',
path: 'selectAccount',
component: SelectCompany,
meta: {
authorization: true,
title: 'Select Account'
}
},
{
name: 'createCustomer',
path: 'create',
component: NewCustomerAccount,
meta: {
authorization: true,
title: 'Create Account'
}
}
]
},
{
path: '/:companyId',
component: Base,
meta: {
authorization: true,
nav: 'account'
},
children: [
{
name: 'home',
path: 'tasks',
alias: '',
component: TaskManager,
meta: {
title: 'My Tasks'
},
},
...
]
}
This happens no matter what method I use to cause navigating, whether I use a router-link or whether I call router.push() in code. However the error always comes from a router-link. If I hide all router-links on the page the navigation works flawlessly. I tried to recreate this on a smaller scale app and I can't seem to make it happen, which means I am doing something wrong but I can't figure it out. I also can't find any similar issues here, which is typically a good indicator that I'm doing something wrong. There is definitely a work-around, where I can store that companyId in a Vuex store and pass it around in the route, but why should I have to pass in a parameter that is not actually in the route?! I really don't want to go down that route (pun intended) unless I absolutely have to. And I first ran into this problem with a child route of the company which needs a projectId parameter. I had the same issue when navigating away from /[:companyId]/[:projectId]/anywhere to /[:companyId]/anywhere IF and only if there is a router-link displayed on the page that relies on [:projectId], and in that situation I was actually relying on whether or not projectId existed within the route params to control a navigation menu. I developed a work around for that behavior but otherwise passing the projectId into the router push to keep the error from happening would have stopped my nav menu from updating correctly.
Is the problem that I do not explicitly define the dynamic route in the parameter? It seems like explicitly defining it would solve my problem but it also requires me to store that somewhere, effectively duplicating the data. I would rather have the id defined in one place (the route) rather than storing it in the store and the route and having to worry about keeping them in sync with each other. Is there no other way?
Any help is appreciated. Thanks.
As is normally the case when I ask a question I discover the answer while asking it. Just posting in case anyone else runs into this same issue. The solution is just to make sure that you explicitly provide the dynamic param when you declare the router-link. Not sure if I like that it lets you create the link without a warning that the required param has not been declared (while there is a warning if vue-router can't resolve the route).
My revised router-link:
<router-link
:to="{
name:'users',
params: {
companyId: route.params.companyId
},
query: {
selected: person.id,
area: 'Info'
}
}">
{{ person.name }}
</router-link>
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
Having an issue with my url updating but the page not.
From the home page, I display a list of projects. Clicking a project will take you to "website.com/project/project-1" and everything works as intended.
However, at the bottom of that page, I again show a list. This list is the same as homepage, with same functionality. But the problem is, is that it will update the url to "website.com/project/project-2" but the page will not re-render or change.
An example of my code
My current router-path of the component.
path: '/project/:project_slug',
name: 'ProjectPage',
component: ProjectPage
My Router Link from the project page to the new project page
<router-link :to="{ name: 'ProjectPage', params: {project_slug: projectHighlightSlug} }">
<h4 class="header-17 semibold">{{projectTitle}}</h4>
</router-link>
Update
This is my current method/watch section
methods: {
goToProject() {
this.$router.push({
name: 'ProjectPage',
params: {project_slug: this.projectHighlightSlug}
})
},
},
watch:{
// eslint-disable-next-line no-unused-vars
'$route'(to, from) {
this.goToProject();
}
}
However, the to,from is "defined but never used" and clicking my button to call goToProject() gives me the error;
"You may have an infinite update loop in watcher with expression "$route""
As explained in the Vue Router docs, when the url the user navigates to uses the same component, it uses the same instance of that component. The docs therefore recommend to listen to $route changes or to use the beforeRouteUpdate navigation guard.
You need to watch the routes to update your page. see code below
watch:{
'$route' (to, from) {
this.goToProject()
// call your method here that updates your page
}
},
source dynamic route matching
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.
I'm using the Element UI NavMenu with :router="true". It is working fine when I click on menu links (route changes and active menu item changes). The issue I'm having is that when I click on the browser navigation buttons (back and forward), the route and component change, but the NavMenu active tab does not change.
Is there an easy way to make sure the NavMenu and current route stay in sync with each other when using the browser navigation buttons? I'm using vue-router with mode: 'history'. I would have thought that this would be handled automatically.
I originally tried to implement this answer with no luck. I now have a working solution for this issue. In my navigation component, I have an el-menu with :router="true" and:default-active="activeLink"`.
Since I have a fairly simple Vue application, I did not want to loop over my router paths and build the NavMenu dynamically. This is a good practice, but I wanted to understand how it works at a basic level first.
From the element-ui docs, default-active controls the index of currently active menu. I added activeLink as a data property:
data() {
return {
logo: logo,
activeLink: null,
}
},
and then added a watch property as described in the gist linked above:
watch: {
$route (to, from) {
this.activeLink = to.path;
}
},
The part I was missing was that the index and the route properties of the el-menu-item need to be the same. Also, we can add a mounted method to make sure that the correct nav link is made active no matter what path we load the app from:
mounted: function(){
this.activeLink = this.$route.path;
},
That fixed the issue of the NavMenu getting out of sync when I use browser navigation buttons.
This was a pain to get to work. I couldn't get beforeRouteUpdate() to work at all, and :default-active="$route.path" almost works, but not if you have parameters for your routes. My current solution is to name all of my routes, and add menu items where the index is the name. Then the default-active value can just be taken from $route.name.
<el-menu :default-active="$route.name" #select="menuSelect">
<el-menu-item index="summary">
<span slot="title">Summary</span>
</el-menu-item>
<el-menu-item index="memory">
<span slot="title">Memory Overview</span>
</el-menu-item>
...
</el-menu>
And in your component:
public menuSelect(index: string) {
this.$router.push({
name: index,
});
}
You can also avoid the annoying error Navigating to current location ("summary") is not allowed like this:
public menuSelect(index: string) {
if (this.$route.name !== index) {
this.$router.push({
name: index,
});
}
}