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
Related
I have an app with a Login.vue and Home.vue files. Because I converted an admin HTML website to a vue 3 app, my javascript only works with page reload. When creating the app I selected add router for SPA maybe I shouldn't have. Up to this point, the views are working except when I redirect from login to home without reloading. Since it is not reloading, my navbar or any JS-dependent functions won't work. how do I redirect from login to home with page reload? Currently, I have the below code but still not working.
this.$router.push({
path: "/admin/home",
reload: true
});
You can use this.$router.go() with empty arguments to reload the page. In combination with this.$router.push({ path: '/admin/home' }) you can achieve it without using vanilla JS features.
<template>
<button #click="redirectReload">Redirect & Reload</button>
</template>
<script>
export default {
methods: {
redirectReload() {
this.$router
.push({ path: '/expedition' })
.then(() => { this.$router.go() })
}
}
}
</script>
Notice how I used .then after $router.push(). Without then the page reloads too quickly, leaving no time for the route to change.
As a bonus, it lets you use all the features of $router.push (for example using arguments like { name: 'home' }.
Vue Router not reload page when you navigate to new URL.
You can try this code for you issue:
const url = new URL('/admin/home', window.location.origin)
window.location.href = url.toString()
Hope this help
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
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.
Without reloading the whole page I need to reload the current route again (Only a component reload) in a vue app.
I am having a path in vue router like below,
{
path: "/dashboard",
name: "dashboard",
component: loadView("Dashboard"),
},
When user clicks on the Dashboard navigation item user will be redirected to the Dashboard page with vue router programmatic navigation
this.$router.push({ name: "dashboard" });
But when user already in the dashboard route and user clicks the Dashboard nav item again nothing happens. I think this is vue router's default behaviour. But I need to force reload the Dashboard component (Not to refresh the whole page).
I can't use beforeRouteUpdate since the router is not updated. Also I have tried the global before guards like beforeEach. But it is also not working.
How can I force reload the dashboard component without reloading the whole page?
It can be done in two ways.
1) Try doing vm.$forceUpdate(); as suggested here.
2) You can take the strategy of assigning keys to children, but whenever you want to re-render a component, you just update the key.
<template>
<component-to-re-render :key="componentKey" />
</template>
<script>
export default {
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
}
</script>
Every time that forceRerender is called, the prop componentKey will change. When this happens, Vue will know that it has to destroy the component and create a new one.
What you get is a child component that will re-initialize itself and “reset” its state.
Not mentioned here, but as the offered solutions require a lot of additional work just to get the app to render correctly, which imo is a brittle solution.. we have just implemented another solution which works quite well..
Although it is a total hack.
if (this.$route.name === redirect.name) {
// this is a filthy hack - the vue router will not reload the current page and then have vue update the view.
// This hack routes to a generic page, then after this has happened the real redirect can happen
// It happens on most devices too fast to be noticed by the human eye, and in addition does not do a window
// redirect which breaks the mobile apps.
await this.$router.push({
name: RouteNames.ROUTE_REDIRECT_PLACEHOLDER
});
}
... now continue to do your normal redirect.
Essentially, redirect to a placeholder, await the response but then immediately continue to another page you actually wanted to move toward
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,
});
}
}