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.
Related
How to pass an object as props in Vue 3 using the Vue Router? This can be achieved in Vue 2 following the accepted answer here and its corresponding JSFiddle. In Vue 3 this syntax stringifies pushed objects into "[object Object]" in contrast to Vue 2. I have tried with Vue 3 in the following fiddle.
const Home = { template: '<div>Home</div>',
mounted: function() {
this.$router.push({
name: "about",
params: {
user: "User Name",
objectParam: {a: "a"}
}
})
}}
const About = { template: '<div>About</div>',
props: ['user', 'o', 'objectParam'],
mounted: function(){
console.log( "Passed props:" )
console.log( this.$props )
}}
const routes = [
{ path: '/', component: Home },
{ path: '/about', name:"about", component: About,
props: route => (
console.log("Params defined here are passed as objects."),
console.log("But route params object passed via push has already been stringified."),
console.log(route.params),
{o: {b: "b"},
...route.params
})},
]
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes,
})
const app = Vue.createApp({})
app.use(router)
app.mount('#app')
Can objects be passed via Vue Router push() in Vue 3?
I can't find any official documentation from Vue Router to support that and to be honest I don't think it's a great idea to send dynamic objects as params, but if you really want to do so, one way to achieve it is to stringify your object and then parse it on the receiver side. So something like this:
this.$router.push({
name: "about",
params: {
user: "User Name",
objectParam: JSON.stringify({ a: "a" })
}
})
And then in the other component:
JSON.parse(this.$route.params.objectParam)
I'm going out on a limb and saying I don't think that's possible in Vue3.
You might take a look into a state manager like Vuex and see if it solves your problem. The state manager concept will allow you to keep a complex object across multiple views.
I am using vue 2.6.10 and in my vue's router.js I set some routes like so
Vue.use(Router)
const router = new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: '/login',
name: 'login',
component: Login,
},
{
path: '/shoes',
name: 'shoes',
component: Shoes,
beforeEnter: (to, from, next) => {
if (store.state.login.quad_token == null) {
next('/login');
}
next();
},
children:[
{
path: ':skateshoes',
name: 'skateshoes',
component: SkateShoes
},
//more children routes
The issue is that if I manually remove the cookie from my browser and got to /shoes/skateshoes I dont get redirected to the login page.
To get redirected, I have to edit the skateshoes children route like so
{
path: ':skateshoes',
name: 'skateshoes',
component: SkateShoes,
beforeEnter: (to, from, next) => {
if (store.state.login.quad_token == null) {
next('/login');
}
next();
}
},
I thought that putting beforeEnter in the parent will also work for all the children. Apparently this does not work , at least for me.
How do I make it work? Thanks
You should use beforeEach guard instead of beforeEnter.
beforeEnter is a per route guard, it doesn't apply for children.
You can use router's meta combine with beforeEach to add conditional logic.
yes it wouldn't work like that, because 'beforeEnter' guards is per-route guard.
If you want to use something global , then you can use beforeEach
Here you can read , how to implement it in your case : https://router.vuejs.org/guide/advanced/navigation-guards.html
Hope, this could helps you :)
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 });
}
How do I create the routes array dynamically, after fetching it via ajax?
Is there a way to add/push new routes to the router after it has been initialized?
This doesn't work:
new Vue({
el: '#app',
template: '<App/>',
data: {
content: []
},
created: function () {
this.$http.get('dummyjsondatafornow').then((response) => {
// this doesn't work when creating the VueRouter() outside the Vue instance, as in the docs.
// this.$router.options.routes.push({ path: '/about', component: About })
let routes = [
{ path: '/about', component: About }
]
// this doesn't work either
this.router = new VueRouter({
routes: routes
})
})
},
// router: router,
components: { App }
})
I don't believe there is no.
That said you can wildcard the route so that may provide you with an alternative.
I built a site where the backend (and in turn pages created) were controlled via a CMS which served all pages to Vue as JSON. This meant Vue wasn't aware of the routes the backend was creating.
Instead we passed all the CMS pages to Vue Router via a single * wildcard component. In Vue Router 2 this would look like:
const routes = [
{ path: '*', component: AllPages }
]
Vue Router 2 allows for Advanced Matching Patterns
These allow you to set a wide variety of conditions, therefore whilst you can't inject the object passed back via ajax into your router you can add a dynamic component to an AllPages component that is wildcard matched. This would allow you to pass the name of the component to load via your ajax request and then load that component when the page is called. i.e.
Your Ajax response:
{
// url: component name
'/about/': 'about',
'/about/contact/': 'contact',
...
}
Then in an AllPages vue component:
<template>
<component v-bind:is="currentView"></component>
</template>
<script>
module.exports = {
data () {
return {
currentView: '',
ajaxRoutes: {}, // loaded via ajax GET request
...
}
},
// watch $route to detect page requests
watch: {
'$route' (to, from) {
if (this.ajaxRoutes[to]) {
this.currentView = this.ajaxRoutes[to]
}
}
},
...
}
</script>
The above is a rather abbreviated idea but essentially you dynamically load the component based on the path the user requested.
I think this is fixed in version 2.3.0. You can now run
router.addRoutes(routes);
to dynamically add routes.
https://github.com/vuejs/vue-router/commit/0e0fac91ab9809254174d95c14592e8dc2e84d33
I have the same situation wherein my routes are built on the backend as it is maintained thru a CMS. With that, I was able to retrieve my routes thru an API call then return it on the vue router. Here's my take:
routes.js
const router = store.dispatch('cms/fetchRoutes').then((response) => {
const router = new VueRouter({
routes: response,
mode: 'history',
...
});
...
return router;
});
export default router;
main.js
import router from './router';
....
router.then((router) => {
const app = new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app')
...
});
Basically I do an axios call to fetch my routes then inject the response to the VueRouter routes property. Then on the main.js, do another then and inject the return on the Vue.
By then, my menus are now being retrieved from the database. No more hard coded paths.