Related
I have a vue-router that works as designed, but the moment I add the beforeEach() function to it, nothing appears in the <router-view> tags. Even if the beforeEach() function is empty, it still hides the content from the page. I there something else I need to do to make the <router-view> tag and router work with each other even with the beforeEach() function?
Here is the router:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
meta: {
authenticatedRoute: false
}
},
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "login" */ './views/Login.vue'),
meta: {
authenticatedRoute: true
}
}
]
})
router.beforeEach((to, from, next) => {
// test
})
export default router;
And the <router-view> tag:
<v-content>
<router-view></router-view>
</v-content>
If your beforeEach fails to call next(), the route will not resolve.
With that in mind, a completely minimal beforeEach would be
router.beforeEach((to, from, next) => {
next()
})
From the documentation...
next: Function: this function must be called to resolve the hook.
...
Make sure to always call the next function, otherwise the hook will never be resolved.
eg: the changed route is https://stackoverflow.com/question#hello
router.push(location, onComplete?, onAbort?)
name is required in localtion
Vue Router allows you to completely customize the scroll behavior on route navigation. Vue scroll behavior is a wide topic, so you can dive into docs
For your example I think you need hash prop, with scroll behavior:
Router.push({ name: routeName, hash: '#toHash' })
router.push({ name: 'question', hash: '#hello' }) can work
For Router.push({ name: routeName, hash: '#toHash' }) to work, you need to configure your vue router.
// router.js file
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
// Your Routes
],
// Ref: https://router.vuejs.org/guide/advanced/scroll-behavior.html
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
// This ensures that if hash is provided to router.push it works as expected.
// & since we have used "behavior: 'smooth'" the browser will slowly come to this hash position.
return {
el: to.hash,
behavior: 'smooth',
}
}
}
});
This code assumes you are using vue-router v4.
I am trying to scroll to an anchor on a page using Vue and Vue Router (with history mode).
When on the index page, the scroll behaviour works as expected by jumping to the section.
However, when I am another page, it loads the index page at the top and not where the anchor is pointing to.
I’m sure it’s a very simple thing but can’t get my head round it!
Any help is appreciated!
My router index:
export default new Router({
scrollBehavior: function(to, from, savedPosition) {
if (to.hash) {
return {selector: to.hash}
} else {
return {x: 0, y: 0}
}
},
mode: 'history',
routes: [ ... ]
})
My Navigation:
<router-link #click.native="closeNav" to="/#enter">Enter</router-link>
<router-link #click.native="closeNav" to="/#prizes">Prizes</router-link>
<router-link #click.native="closeNav" to="/#faqs">FAQ</router-link>
<router-link #click.native="closeNav" to="/contactus">Contact</router-link>
Vue Router v3.x
This is a bit of an old question and OP has almost surely found a solution already, but for anyone running into this problem, this should do the trick:
<router-link :to="{ name: 'Homepage', hash: '#enter' }">Enter</router-link>
<router-link :to="{ name: 'Homepage', hash: '#prizes' }">Prizes</router-link>
<router-link :to="{ name: 'Homepage', hash: '#faqs' }">FAQ</router-link>
<router-link :to="{ name: 'Contact' }">Contact</router-link>
This should allow you to have these links accessible from other views/components, and when clicked will redirect you to the named route (Homepage in this case), and scroll to the hash specified (#enter, #prizes, #faqs).
In addition to the router code snippet in the question, you can add smooth scrolling using the native window.scrollTo method:
export default new Router({
routes: [],
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return window.scrollTo({
top: document.querySelector(to.hash).offsetTop,
behavior: 'smooth'
})
} else {
return { x: 0, y: 0 }
}
}
})
Update for Vue Router v4.x
You write your router-links the same, but you can write the scroll behavior and element selection a bit neater now. From the docs
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
behavior: 'smooth'
}
}
}
})
I am using scrollIntoView() instead of window.scrollTo()
export default new Router({
routes: [],
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return document.querySelector(to.hash).scrollIntoView({ behavior: 'smooth' });
} else {
return savedPosition || { x: 0, y: 0 }
}
}
})
I can set scrolling behaviour to Vue.js Router like this:
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'index',
component: Main
},
{
path: '/some-path',
name: 'some-path',
component: SomePath
}
],
scrollBehavior() {
return {x: 0, y: 0}
}
})
This works perfectly when you click on the link with some page which is not current. When I click on the link which is already rendered, i.e. in the footer, nothing happens. Vue Router assumes there is no state transition. What is the preferred way to scroll up in this case?
You can't do this through vue-router, but you can add a scroll-to-top method to every router-link.
Just create a method like this:
methods: {
scrollToTop() {
window.scrollTo(0,0);
}
}
Add it to the link:
<router-link #click.native="$scrollToTop">
If you want to use it outside of your footer too, it's better to add it to the Vue prototype
Vue.prototype.$scrollToTop = () => window.scrollTo(0,0)
It's not a 100% solution but it's the simplest one
I couldn't get any of the above solutions working, and it was really frustrating.
What ended up working for me was the below:
const router = new Router({
mode: 'history',
routes: [...],
scrollBehavior() {
document.getElementById('app').scrollIntoView({ behavior: 'smooth' });
}
})
I mount my VueJs app to #app so I can be certain it is present and is available for selection.
You could make use of behavior: smooth:
moveTo () {
let to = this.moveToDown
? this.$refs.description.offsetTop - 60
: 0
window.scroll({
top: to,
left: 0,
behavior: 'smooth'
})
this.moveToDown = !this.moveToDown
}
The best solution I've found for this is: https://router.vuejs.org/guide/advanced/scroll-behavior.html
Specifically:
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
})
Expanding on the answer from Vitaly Migunov, you can instead add directly from the router a scrollTo method to the window object. This way you won't need to add the function to every router link.
const router = new Router({
mode: 'history',
routes: [...],
scrollBehavior() {
window.scrollTo(0,0);
}
})
Use refs for scroll to certain section
<template>
<body>
<div ref="section">
// Your content section
</div>
</body>
</template>
export default class MyPage extends Vue {
$refs!: {
section: HTMLFormElement;
};
scrollToTop() {
this.$refs.section.scrollTo(0, 0);
}
}
This worked for me:
const router = createRouter({
history: createWebHashHistory(),
routes,
scrollBehavior() {
document.getElementById('app').scrollIntoView({behavior:'smooth'});
}
})
Basically, I think you want to scroll to the top of the page, unless an internal page location is specified (e.g. www.example.com/home#blah). All of the answers so far would ignore this location parameter, and just scroll to the top anyway.
scrollBehavior(to, from, savedPosition) {
//If there is no hash parameter when we change vue, scroll to top of page
if (!to.hash) {
return { x: 0, y: 0 }
}
}
We can check if there is a location parameter using to.hash, then only scroll to the top if no hash location is present.
for vue3
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
scrollBehavior(to, from, savedPosition) {
// always scroll to top
return { top: 0 }
},
})
For Vue3 you should use scrollBehavior. Use left and top instead of x and y.
scrollBehavior(to, from, savedPosition) {
return { left: 0, top: 0, behavior: "smooth" };
}
I've tried all of the above answers and none did work for me; However I've tried this one and it did the job for me; Add this to your App.vue file
updated() {
this.$refs.main.scrollTo(0, 0)
},
Vue Js have inbuilt support for scrolling if the browser supports history.pushState.
It is very easy to configure, Just provide the scrollBehavior function, when creating Vue router instance like below:
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// page scroll to top for all route navigations
return { x: 0, y: 0 }
}
})
For more options and detail about Vue Scroll Behavior click here
Use this nice component: https://www.npmjs.com/package/vue-backtotop
<back-to-top text="Back to top"></back-to-top>
Alternatively, this also worked for me:
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
scrollBehavior(to, from, savedPosition) {
document.getElementById('app').scrollTop = 0
},
routes
})
I'm rewriting an existing Angular 1 application with Vue.
The application always needs to authenticate an user by locale, id and token before entering any views. Respecting the conventions of our API, I specified the token as a query parameter within my main parent route.
Coming from the existing Angular's UI router implementation I thought this is the way to go:
// main.js
new Vue({
el: '#app',
router,
store,
template: '<router-view name="main"></router-view>'
})
// router.js
const router = new Router({
mode: 'history',
routes: [
{
name: 'start',
path : '/:locale/:id', // /:locale/:id?token didn't work
query: {
token: null
},
beforeEnter (to, from, next) {
// 1. Get data from API via locale, id and token
// 2. Update store with user data
},
components: {
main: startComponent
},
children: [{
name: 'profile',
path: 'profile',
components: {
main: profileComponent
}
}]
}
]
})
When I navigate to the profile view, I expect the view to change and the query token to stay, e.g. /en-US/123?token=abc to /en-US/123/profile?token=abc. Neither happens.
I'm using Vue 2.3.3 and Vue Router 2.3.1.
Questions:
Can I keep query parameters when navigating to child routes?
Am I using the Vue router right here? Or do I need to blame my UI router bias?
You can resolve this in the global hooks of Router
import VueRouter from 'vue-router';
import routes from './routes';
const Router = new VueRouter({
mode: 'history',
routes
});
function hasQueryParams(route) {
return !!Object.keys(route.query).length
}
Router.beforeEach((to, from, next) => {
if(!hasQueryParams(to) && hasQueryParams(from)){
next({name: to.name, query: from.query});
} else {
next()
}
})
If the new route (to) does not have its own parameters, then they will be taken from the previous route (from)
You can add in a mounted hook a router navigation guard beforeEach like this preserveQueryParams:
// helpers.js
import isEmpty from 'lodash/isEmpty';
const preserveQueryParams = (to, from, next) => {
const usePreviousQueryParams = isEmpty(to.query) && !isEmpty(from.query);
if (usePreviousQueryParams) {
next({ ...to, query: from.query });
} else {
next();
}
};
// StartComponent.vue
removeBeforeEachRouteGuard: Function;
mounted() {
this.removeBeforeEachRouteGuard = this.$router.beforeEach(preserveQueryParams);
}
// don't forget to remove created guard
destroyed() {
this.removeBeforeEachRouteGuard();
// resetting query can be useful too
this.$router.push({ query: undefined });
}