Notice: The Monaca & Onsen UI Community Forum is shutting down.
For Onsen UI bug reports, feature requests and questions, please use the Onsen UI GitHub issues page. For help with Monaca, please contact Monaca Support Team.
Thank you to all our community for your contributions to the forum. We look forward to hearing from you in the new communication channels.
Vue-router + Vuex + Stack navigator
-
I would like to share with you an alternative that I developed to join the vue-router and the Onsen navigator.
I have not yet integrated 100% with all the features of the vue-router, but it is already possible to navigate between the routes managing the stack navigation.
Based on Kitchensink (https://github.com/OnsenUI/vue-onsenui-kitchensink/blob/master/src/store.js), I created my navigation store too.
export default { strict: true, namespaced: true, state: { goingBack: false, // Its used when user click on back button stack: [], options: { animation: 'slide' } }, mutations: { push (state, page) { state.stack.push(page) }, pop (state) { state.goingBack = false if (state.stack.length > 1) { state.stack.pop() } }, replace (state, page) { state.stack.pop() state.stack.push(page) }, reset (state, page) { state.goingBack = false state.stack = [page || state.stack[0]] }, options (state, newOptions = {}) { state.options = newOptions }, toggleGoingBack (state, shouldGoingBack) { if (typeof shouldGoingBack === 'boolean') { state.goingBack = shouldGoingBack } else { state.goingBack = !state.goingBack } } } }
I used
goingBack
to router knows when the user is using a route with same level but is going back.And my app component is:
<template lang="pug"> v-ons-splitter v-ons-splitter-side(swipeable collapse="" width="260px" :open.sync="splitterIsOpen") menu-page v-ons-splitter-content v-ons-navigator( :page-stack="pageStack" :pop-page="storePop" :options="options") </template> <script> import MenuPage from './pages/Menu' export default { computed: { pageStack () { return this.$store.state.navigator.stack }, options () { return this.$store.state.navigator.options }, splitterIsOpen: { get () { return this.$store.state.splitter.open }, set (newValue) { this.$store.commit('splitter/toggle', newValue) } } }, methods: { storePop () { this.$store.commit('navigator/toggleGoingBack') if (window.history.length <= 2) { this.$router.push({ name: 'home' }) } else { this.$router.go(-1) } } }, components: { MenuPage } } </script>
And after this, I need to tell the router how his work will stack navigation:
import Vue from 'vue' import Router from 'vue-router' import store from '@/store' import HomePage from '@/pages/Home' import HostPage from '@/pages/Host' import HostItemPage from '@/pages/HostItem' Vue.use(Router) const router = new Router({ routes: [ { path: '/', name: 'home', component: HomePage }, { path: '/host/:profile?', name: 'host', component: HostPage, children: [ { path: 'items/:item?', name: 'host-item', component: HostItemPage } ] } ] }) router.beforeEach((to, from, next) => { store.commit('splitter/toggle', false) // When user access root path, clear stack before ends request if (to.path === '/') { store.commit('navigator/reset', HomePage) return next() } // Every time when stack is empty, push `HomePage` as first if (store.state.navigator.stack.length <= 0) { store.commit('navigator/push', HomePage) } // Based on https://github.com/vuejs/vue-router/blob/dev/examples/transitions/app.js#L21 const toDepth = to.path.split('/').length const fromDepth = from.path.split('/').length if (from.name && (toDepth < fromDepth || store.state.navigator.goingBack)) { store.commit('navigator/pop') } else { if (toDepth === fromDepth) { const item = to.matched[to.matched.length - 1] store.commit('navigator/push', item.components['default']) } else { store.commit('navigator/reset') to.matched.forEach((item) => { store.commit('navigator/push', item.components['default']) }) } } next() }) export default router
-
Refactoring my code:
router.beforeEach((to, from, next) => { store.commit('splitter/toggle', false) // When user access root path, clear stack before ends request if (to.path === '/') { store.commit('navigator/reset', HomePage) return next() } const toMatch = to.matched[to.matched.length - 1].components['default'] const toParentMatch = to.matched[to.matched.length - 2] ? to.matched[to.matched.length - 2].components['default'] : null const fromParentMatch = from.matched[from.matched.length - 2] ? from.matched[from.matched.length - 2].components['default'] : null if (from.name && (toMatch === fromParentMatch || store.state.navigator.goingBack)) { store.commit('navigator/pop') } else if (toParentMatch === fromParentMatch) { store.commit('navigator/push', toMatch) } else { const stack = to.matched.map((item) => item.components['default']) store.commit('navigator/reset', [HomePage].concat(stack)) } next() })
-
Dear raphox,
I would like to know more about this and maybe a link to a working project on github would be great !
-
@raphox Hey, finally got some time to test vue-router + navigator. I think
v-ons-navigator
is quite robust and is easy to plug vue-router. What do you think about this?router.beforeEach((to, from, next) => { store.commit('navigator/reset', to.matched.map(m => m.components.default)); next(); });
It passes the components in the new route to the navigator and it figures about the rest. The
map
function might need to be changed depending on the app. This example specifically assumes every route is a page (no small child views) and all of them use the default view.Alternatively, it can also be used without Vuex in the same place where you declare your
pageStack
array:watch: { '$route' (to, from) { this.pageStack = to.matched.map(m => m.components.default); } }
The back buttons can simply run
this.$router.go(-1)
if the routes are linear (always parent -> child) orthis.$router.push({name: this.$route.matched[this.$route.matched.length - 2].name})
if the routes can switch between sibling routes (routeA/child1
-> push ->routeA/child2
-> pop ->routeA/
).The performed animations depend on the
pageStack
length and the current top page. If the top page changed and the new route length is lower than the previous one, it will show a pop animation. Anything else is considered a push (or replace, with push animation). Therefore, if moving among child views (routeA/child1/
->routeA/child2/
), one should replace the routes. Otherwise, it will end up doing another push animation when going back withgo(-1)
since the length doesn’t change. I guessv-ons-tabbar
is a better fit for that kind of route.v-ons-navigator
should only take care of push-pop routes (routeA/child1
->route1/child1/child1-1/
).
-
vue-onsenui
+vue-router
example here without Vuex: https://frandiox.github.io/onsenui-vue-router
-
@Fran-Diox Hi.
I think your code is simple but really works.
In my second code Im giving attention to:
- direct access by url without referer. Like you.
- store home page on the stack of pages
- links with
history.back()
- controll push or pop to animation based on level route access
@Eric-Hughes I will create. Thanks for your interest.