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
Related
I have encountered a problem with Vue router recently, imagine that we have a Vue CLI project and our App component is like below:
<template>
<div id="app">
<div class="links">
<router-link to="one">one</router-link>
<router-link to="two">two</router-link>
</div>
<div class="routers">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
data: function(){
return{
}
},
created(){
this.$router.push({
name: 'two',
params:{
message: 'hello'
}
});
}
}
</script>
Our one and two components are:
<template>
<div>
two, the message is {{ message }}
</div>
</template>
<script>
export default {
props:[
"message"
]
}
</script>
and
<template>
<div>
one
</div>
</template>
and our router file is:
import Vue from 'vue'
import VueRouter from 'vue-router'
import one from '../components/one.vue'
import two from '../components/two.vue'
Vue.use(VueRouter);
export const router = new VueRouter({
routes:[
{
path: '/one',
name: 'one',
component: one
},
{
path: '/two',
name: 'two',
component: two,
props: true
}
]
});
The problem is, when I open the page for the first time, everything is fine and the second component recognizes the prop and shows "two, the message is hello". the router links all work fine when I click on them and the prop is passed properly.
The problem appears when I refresh the page, and it only shows "two, the message is".
What I have done to solve this: It seems that this.$router.push is not working correctly after the second page refresh, and the reason is the duplicated navigation error which doesn't let you navigate to the same route.
The questions are:
Did I recognize the problem correctly? Is it because of the duplicated navigation?
If that's the problem, how can I make a router component to always mount on the page refresh, with the prop passed to it properly?
Route params that are not included in the path (eg /route/:param) do not persist on page reload. They live only in-memory for the current session.
What I would do instead is
Remove the created hook in your App component
Set up a redirect from / to two in your router
{
path: "/",
redirect: { name: "two", params: { message: "hello" } }
}
Set a default value for the prop in two to handle reloads
props: {
message: {
type: String,
default: "hello"
}
}
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)
}
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>
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>
I am using Vue and Vue-router.
routes
{
path: '/admin', component: AdminPage,
children: [
{path: 'admin-user', component: AdminUser, name: 'admin-user'}
]
},
In the AdminPage component I have router links and its router view to navigate between pages. One of the route is of path admin-user, which uses the AdminUser component.
AdminPage.vue:
<template>
<div id="adminPage">
<ul id="adminNavList">
<li>... some nav links ...</li>
<li><router-link :to="{ name: 'admin-user', params: {front: true}}">User</router-link></li>
</ul>
<div class="adminViewPort" v-if="isAdmin">
<router-view></router-view>
</div>
</div>
</template>
In the AdminUser component there are two components which are displayed based on the conditions. When the route path is first loaded and the AdminUser is created, the AdminUserList component is displayed by default. And when clicks the button which fires show_user_bookings() button, the other component AdminUserBookings is displayed. Similarly when the user clicks the button which fires show_user_list() method, the AdminUserList component is displayed.
AdminUser.vue
<template>
<div id="AdminUserWrapper">
<admin-user-list v-if="component_name==='user_list'" :showUserBookings="show_user_bookings"></admin-user-list>
<admin-user-bookings v-if="component_name==='user_bookings'" :show_user_list="show_user_list"></admin-user-bookings>
</div>
</template>
<script>
import AdminUserList from './AdminUserList.vue'
import AdminUserBookings from './AdminUserBookings.vue';
export default {
name: 'AdminUserWrapper',
components: {
AdminUserList, AdminUserBookings
},
data() {
return {
component_name: '',
},
methods: {
show_user_list() {
this.component_name = 'user_list';
},
show_user_bookings() {
this.component_name = 'user_bookings';
}
},
created() {
this.show_user_list();
}
}
</script>
So the problem is, if AdminUserBookings component is displayed and clicks the link for the path admin-user, the default component AdminUserList is not displayed. Is there any way to display the default component AdminUserList whenever the admin-user path is requested from the AdminPage even if its already in the admin-user path when the AdminUserBookings component is displayed?