Vue Router scroll to id with extra space on top - vue.js

In my Vue app, I have a fixed header of approximately 100px height.
I have a page where I have set an in page navigation to specific sections (= navigation to id's).
The navigation is working (when I click the anchor tag, it snaps to the correct section), but the top part of each section is always hidden underneath my header.
How can I set the scroll behavior of Vue Router, so the browsers scrolls to the section with for example 100px extra space on top?
Also, the 'smooth' behavior is not working either.
I have set my VueRouter as follows:
const router = new VueRouter({
routes,
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash,
behavior: 'smooth',
x: 0,
y: 100,
};
} else {
return { x: 0, y: 0 };
}
},
});
My in page navigation is set as follows:
<div class="in-page-nav" v-if="applicationDetails">
<span>Shortcuts:</span>
<nav>
<router-link to="#application-details">Application details</router-link>
<router-link to="#application-answers">Application answers</router-link>
<router-link to="#jury-members">Jury members</router-link>
<router-link to="#jury-chats">Jury chats</router-link>
<router-link to="#applicant-chats">Applicant chats</router-link>
<router-link to="#voting-form" v-if="votingStatuses && openForVotes">
Make new vote</router-link>
<router-link to="#additional-info">Additional info</router-link>
<router-link to="#file-upload">Upload file</router-link>
<router-link to="#downloads" v-if="usrFileLink">Downloads</router-link>
</nav>
</div>

Related

Quasar QTable lost state after returning from child component

I have a QTable that renders link where user can click into detail page and use back button to return to this table later. If I move to page 2 and click on the link to view detail page, on return, the table state is lost and it's showing page 1 again.
I've tried using Keep-Alive in hope that component will not get destroyed but it didn't help.
How do I get around this problem?
If you wrap the table within keep-alive, it will be kept alive as long as that page is alive. But, since you are navigating into another page, the table will be destroyed along with the page that contains it.
So, to achieve your aim, you must keep the whole page alive. You can do it like this:
<template>
<q-page>
<!-- ... -->
<q-table ... />
<!-- ... -->
</q-page>
</template>
<script>
export default {
name: 'ThePageYouWantToKeepAlive',
// ...
}
</script>
keep-alive has some props to manage inclusions/exclusions, you can use include prop to only include the page(s, it accepts a comma-separated list, an array or RegExp) that you want to keep alive. This way you would keep your performance loss to a minimum.
<!-- /src/layouts/MainLayout.vue -->
<!-- ... -->
<q-page-container>
<keep-alive include="ThePageYouWantToKeepAlive">
<router-view></router-view>
</keep-alive>
</q-page-container>
<!-- ... -->
The demonstration below would help to understand the situation better.
// Source: https://codepen.io/yusufkandemir/pen/zYrZOKx?editors=1010
// Adopted from: https://jsfiddle.net/Linusborg/L613xva0/4/
const Foo = {
name: 'foo',
template: '<div><p v-for="n in numbers">{{ n }}</p></div>',
data: function() {
return {
numbers: [Math.round(Math.random() * 10), Math.round(Math.random() * 10)]
}
}
}
const Bar = {
name: 'bar',
template: '<div><p v-for="n in numbers"><strong>{{ n }}</strong></p></div>',
data: function() {
return {
numbers: [Math.round(Math.random() * 10), Math.round(Math.random() * 10)]
}
}
}
const Baz = {
name: 'baz',
components: {
Foo
},
template: '<div><foo></div>'
}
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
{ path: '/baz', component: Baz }
]
const router = new VueRouter({
routes
});
const app = new Vue({
router
}).$mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.2.0/vue-router.min.js"></script>
<div id="app">
<ul>
<router-link to="/foo">
<li>Go to Foo</li>
</router-link>
<router-link to="/bar">
<li>Go to Bar</li>
</router-link>
<router-link to="/baz">
<li>Go to Baz</li>
</router-link>
</ul>
<keep-alive include="foo">
<router-view></router-view>
</keep-alive>
</div>
You need to store the state of the table in the parameters of the route or vuex, localStorage.

VueJS Router prevent back on certains router-link's

Building PWA app with VueJS and I have tabs like navigation component. But instead of show/hide content, navigation is done through Vue Router. Side effect of this navigation is "back" button behavior, every navigation action is logged in browser history, after I visit 4 tabs, and if I want to go back to actual page which was before page with tabs, I need to press "back" 4 or more times (depends on how many times I navigated trough tabs).
What I want to do is something like this:
<router-link no-history="true" to="tab1">Tab1</router-link>
<router-link no-history="true" to="tab2">Tab2</router-link>
<router-link no-history="true" to="tab3">Tab3</router-link>
Of course, I don't want to do it globally. If this even possible?
You need to use router.replace.
From Vue Documentation :
It acts like router.push, the only difference is that it navigates without pushing a new history entry, as its name suggests - it replaces the current entry.
In your case, you would just need to add the replace attribute to your router-link tag :
<router-link to="tab3" replace>Tab3</router-link>
You can find more informations about replace on Vue programmatic navigation doc
add replace to your router-link to avoid stacking routes on the navigation history :
Vue.config.productionTip = false;
Vue.config.devtools = false;
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h5>Navigate between routes then click "previous"</h5>
<button #click="$router.go(-1)">Previous</button>
<p>
<router-link to="/foo" replace>Go to Foo</router-link>
<router-link to="/bar" replace>Go to Bar</router-link>
</p>
<router-view></router-view>
</div>

vue-router transitions with slide not scrolling to the top

I use transitions in vue-router and it slides nice, but I would like to see the coming page from the top, instead, I need to scroll the new page manually to the top and this is not ok.
So I added the option scroll-behavior which should help me:
When using client-side routing, we may want to scroll to top when navigating to a new route, or preserve the scrolling position of history entries just like real page reload does. vue-router allows you to achieve these and even better, allows you to completely customize the scroll behavior on route navigation.
Here my configuration:
Router
const routers = [...]
const router = new VueRouter({
routes: routes,
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
//return savedPosition
return { x: 0, y: 0 } // just for debugging
} else {
return { x: 0, y: 0 }
}
}
});
App.vue
<template>
<div class="home">
<header-comp></header-comp>
<main>
<transition name="slide"
enter-active-class="animated slideInLeft faster"
leave-active-class="animated slideOutRight faster">
<router-view></router-view>
</transition>
</main>
<footer-comp></footer-comp>
</div>
</template>

How to scroll to a specific anchor using a router-link?

I'm trying to develop a simple website using vue js and so far it goes well but I'm facing the following Issue:
I'm using a router to change pages, it works, but what I need to do is: Change Page & scroll to a specific anchor.
What I tried already:
Works well:
Route to contact page or home
<router-link to="/contact">Contact</router-link>
<router-link to="/">Home</router-link>
Doesn't work:
<router-link :to="{ name: '/', hash: '#technology' }" >Technology</router-link>
The last one works only if I'm on the home page, but whenever I change to Contact Page and try to route to "/#technology" it won't work. It does try routing to "http://example.com/contact/#technology" instead of "http://example.com/#technology".
If I do it like that, it will just scroll to top, but not to the anchor:
<router-link to="/#technology" Technology</router-link>
My Router Code:
const routes = [
{ path: '/', component: Home },
{ path: '/contact', component: Contact }
]
const router = new VueRouter({
mode: 'history',
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
}
if (to.hash) {
return { selector: to.hash };
}
return { x: 0, y: 0 }
},
});
new Vue({
router,
}).$mount('#app');
And the templates look like this ( I did remove the unnecessary code):
<template id="home">
<div>
Home
<div id="technology"> <!-- IT SHOULD SCROLL TO HERE -->
</div>
</template>
<template id="contact">
<div>
Contact Page
</div>
</template>
i think you are on the right path.
but maybe you have mistaken the usage of Route name and path.
instead of
<router-link :to="{ name: '/', hash: '#technology' }" >Technology</router-link>
try using path instead of name
<router-link :to="{ path: '/', hash: '#technology' }" >Technology</router-link>

Keeping State of Vue

Is there a way to keep a state of a vue so that when navigated back it is shown?
Example:
1) I'm on Page A, search something, items are loaded, I scroll down and pick item 34 from the list.
2) Now Page B opens with information about the item. I click back but end on an empty Page A.
What I would want is the search results and ideally the scroll position from when I left that vue.
Is that possible out of the box in vue2 or do I have to code all of that myself?
So you want to somehow vuejs to remember to scrollPosition.As always that is very simple with vue.
The scroll behavior can managed by Vue-Router.I'll show you an example.
The Page component with 2 scroll positions
<template>
<div>
<div id="data1" style="height:700px;background:blue">
First scroll position
</div>
<div id="data2" style="height:700px;background:yellow">
Second scroll position
</div>
</div>
</template>
The component which navigate to page component
<template>
<div>
<router-link
tag="button"
:to="link"
>
Navigate to Page in scroll Position 2
</router-link>
</div>
</template>
<script>
export default {
data: () => ({
link: {
name: 'Page',
hash: '#data2' //<= important,this adds the #data2 in url
}
})
}
</script>
Now your route file have to look like this:
import Vue from 'vue'
import Router from 'vue-router'
import Page from '#/components/Page.vue'
import NavigationButton from '#/components/NavigationButton.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/Page',
name: 'Page',
component: Page,
},
{
path: '/',
name: 'NavigationButton',
component: NavigationButton
},
//other routes
],
mode: 'history',
scrollBehavior(to, from, savedPosition) {
if(savedPosition){ //when use press back button will go at the position he was on the page
return savedPosition
}
if(to.hash){ //if has a hash positition to go
return { selector: to.hash } //go to the page in scrolled Position
}
return { x:0, y: 0 } //go to the page in scroll = 0 Position
}
})
That i understood by your question,but i am not sure your asking about that.Anyway if i am right,dont forget also to take a look to official vue-router documentation about scroll behavior.See here