Vue-router redirect to hash with timeout - vue.js

I have route like this:
{
path: '/kontakt',
redirect: '#contact',
component: index
},
and scrollBehavior:
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if(!to.hash) {
return {
x: 0, y: 0
}
}
if(to.hash) {
return {
selector: to.hash
}
}
}
and IMO because I display my page 1 second after user actually enter it (I show logo for 1sec) above code doesn't scroll page to #contact div when I enter /kontakt route.
app.vue
<div v-show="timeGap">
<router-view />
</div>
here timeGap is changed to true after 1sec in setTimeout method. How to fix so it scroll to #contact div after this timeout?

You have to move the control of your scrolling behaviour from your router scrollBehavior handler to your component index and there into the place that knows the logo show state is off.
This might be:
a Vue lifecycle hook (either mounted or updated);
or other point in this component that knows the logo state (watch after computed ?);
As soon as you find that place (watch) in your component you have to check if the logo state is off and do your scroll using this approach.

Related

Vue3 - router push to another page only renders top part of the page - not scrollable

I am experiencing an issue that I actually already have had for quite some time.
My setup is Vue3 and Vuetify 3.
Whenever I change the page after some kind of calculation:
router.push({ name: 'AnotherPage', params: { id: index, variable: x} });
The page is redirected to 'AnotherPage' but the page is not scrollable, so only the part of the page that fits on the page is rendered.
After doing an F5 refresh, the complete page is rendered and scrollable.
I only noticed this behavior when I was looking into redirecting to a certain section on a page, using anchors, and found that it was not working.
scrollToElement() {
if (this.$route.hash) {
const targetId = ref(this.$route.hash.replace('#', ''));
const eal = document.getElementById(targetId.value);
if (eal != null) {
eal.scrollIntoView();
}
}
},
This works when I load the page from scratch, but it doesn't when I use the aforementioned router.push method. There is no error though, so the component is able to find the element linked to the requested anchor tag.
Another thing is that when I perform a hardcoded router.push from a button click, it works!
In vuejs there is a parent-child dependency between elements. It is not clear from the question if views are used, but I assume so, because that's a best practice.
router.push is for history manipulation: so if you do that from the parent main-view it will work, and inform automatically a child-view to re-render (because of the change itself)
but if you do the calculation deeper in the page, in a child, and want to update the entire page, you have to use the emit component event (the child informs the parents about the change of values and for a re-render)
See this example for a demo: https://learnvue.co/tutorials/vue-emit-guide
To put it together: having a update function in the mainView, which wants to be called from a RouterLink in a childView. This is started by updateParent - so there a emit event prev-next-click is defined.
<script setup>
import { RouterLink } from 'vue-router'
</script>
<script>
export default {
emits: ['prev-next-click'],
methods: {
updateParent: function(c, d) {
// on button click update from fixture
// emit a call from subView (child) to run the update in main App (parent)
this.$emit("prev-next-click", c, d);
},
to: function (c=this.category, d=this.today) {
return { name: 'quote', params: { category: c, day: d }}
},
},
created() {
this.updateParent(this.$route.params.category, this.$route.params.day);
}
};
</script>
<template>
<RouterLink class="button prev" :to="to(category,prev)" #click="updateParent(category,prev)">click</RouterLink>
</template>
And the mainView glue it together with RouterView (not the name of the view!)
<script setup>
import { RouterLink, RouterView } from 'vue-router'
</script>
<script>
export default {
methods: {
update: function (c, d) {
console.log("update c", c, "d", d);
},
},
}
</script>
<template>
<RouterView #prev-next-click="update"/>
</template>
Update to the comment:
A problem I had to solve with the code above, was that the childView wasn't rendered properly when initialized. That's why childView always call updateParent once directly during creation by the created hook.
See vuejs lifecycle for all other hooks - maybe the updated fits in your case.

How to stop FOUC when leaving route with 'vue-flickity' carousel/slider?

I'm using the vue-flickity package for MetaFizzy's Flickity in my Vue app. When navigating away from a route that has an instance of vue-flickity slider, you get a brief FOUC showing all the slides unstyled in the document as the <Flickity /> slider is dismounted(?) and the view changes.
I've tried wrapping it in a <keep-alive>, but that doesn't help.
What would be the best approach to hiding or "animating out" the component before the user navigates away from a route?
I also tried to use beforeRouteLeave(), transition opacity on <Flickity ref="mySlider" /> to 0, then change route. I tried something like the following, but it didn't work as expected:
// SliderView.vue
<template>
<Flickity ref="mySlider">
<div v-for="(slide, index) in slides" :key="index">
// slide content markup
</div>
</Flickity>
</template>
<script>
import 'Flickity' from 'vue-flickity'
export default {
name: 'SliderView'
}
</script>
// router.js
import Vue from 'vue'
import Router from 'vue-router'
import SliderView from './views/SliderView.vue'
export default new Router({
routes: [
{
path: '/routeWithSlider',
component: SliderView,
beforeRouteLeave (to, from, next) {
const slider = this.$refs.mySlider
if (slider) {
slider.style.transition = 'opacity .5s'
slider.style.opacity = 0
setTimeout(() => {
next()
}, 600)
} else {
next()
}
}
}
]
})
Is this the correct approach, or should I be approaching this differently?
Also, if there was a way to do this globally for all <Flickity /> instances on all routes without specifying refs, that would also be useful too.
I haven't been using Vue for that long, so am looking for some guidance on the direction to take.
I found the best way to acheive this is to use BeforeRouteLeave() lifecycle hook on the component with the slider to animate the Flickity slider exit before changing route:
beforeRouteLeave (from, to, next) {
const slider = this.$refs.flickity.$el
slider.style.transition = 'opacity .25s ease'
slider.style.opacity = 0
setTimeout(() => {
next()
}, 250)
}

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

Vue-router How to come back to a specific section of the home page?

I have ci-urrently a home page with many sections taht the user can access by scrolling or from a navbar menu-1. In this menu there is also a "Plus" button to request additional pages from the router
All pages called from a router have a navbar menu-2 to call other pages from the router or go back to the home page
It"s working fine...
but I would like to be able from this pages to go back to a specific section of the home page;..
I tried to use :
<div class="navbar-text">
<!-- Button trigger modal-->
<div class="dropdown">
<button class="dropbtn">PLUS...</button>
<div class="dropdown-content">
<a #click="$router.push('/home#about-us')">ABOUT US</a>
<a #click="$router.push('/home#memberships')">MEMBERSHIPS</a>
<a #click="$router.push('/hoem#events')">EVENTS</a>
<a #click="$router.push('/home#portfolio')">PORTFOLIO</a>
<a #click="$router.push('/home#leaders')">LEADERS</a>
<a #click="$router.push('/home#contact')">CONTACT</a>
</div>
</div>
</div>
but no way .. I'm always back to the top of the home page ...
any trick ? thanks for feedback
Check out Vue Router - Scroll Behavior from the docs:
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.
Specially the code before Async Scrolling section
updated the scrollBehavior in my router index.js
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {selector: to.hash}
} else if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
I faced the same problem, You can try this way
In router js file
scrollBehavior(to: any, from: any, savedPosition: any) {
return new Promise((resolve: any, reject: any) => {
setTimeout(() => {
resolve({ x: 0, y: 0 });
}, 480);
});
}
I faced the same problem but the above methods didn't really work very well for me. I had to add an extra tweek to wait for all records to be loaded from the db before attempting the scroll to savedposition.
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (to.hash) {
return resolve({ selector: to.hash });
} else if (savedPosition) {
return resolve(savedPosition);
} else {
resolve(document.getElementById("app").scrollIntoView({ behavior: "smooth" }));
}
}, 1200);
});
},

vuejs route between components scroll same

<template>
<div class="index">
<common-header id="common-header" class="common-header" v-el:commonheader></common-header>
<router-view transition keep-alive class="index-view"></router-view>
</div>
</template>
the route view will show two component A and B, while component A scrollTop is 0, I route to component B, and scroll down, and then route to component A, A is also scroll. Anyone have any ideas?
You can add a global before hook to the router, which will be called before every route transition starts and scroll to the top of the page. That is how I have solved it. Read here
Vue 1.
router.beforeEach(function (transition) {
window.scrollTo(0, 0)
transition.next()
})
For Vue2:
const router = new VueRouter({
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
})
Refer here
For Vuejs2.0, there is a new accepted way dealing with scroll behavior on page changes:
import VueRouter from 'vue-router';
const router = new VueRouter({
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
})
This will scroll to the top of the page after each navigation change. You can read up on the full, official documentation about this functionality here.
I think you are scroll the wrong element.I have make the same mistake like that:component A and component B is both in element Body,and I make the body scroll,so as long as you move the scroll bar,it will work for both A&B.I finally work out it by scrolling component instead of scrolling the body.