Open lights0123 opened 4 years ago
Since they just made the fix so you can add a custom event to handle tab buttons (not yet published to npm), you can do this manually, assuming named views still work with ion-vue-router
.
Vue Router Named Views: https://router.vuejs.org/guide/essentials/named-views.html
<template>
<ion-tabs>
<ion-vue-router v-show="selectedTab === 'tab1'" name="tab1router" />
<ion-vue-router v-show="selectedTab === 'tab2'" name="tab2router" />
<ion-tab-bar slot="bottom">
<ion-tab-button
:selected="selectedTab === 'tab1'"
@click="selectedTab = 'tab1"
>
Tab 1
</ion-tab-button>
<ion-tab-button
:selected="selectedTab === 'tab2'"
@click="selectedTab = 'tab2"
>
Tab 2
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</template>
<script>
export default {
data() {
return {
selectedTab: 'tab1'
}
}
};
</script>
Router config
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
tab1router: Tab1Component,
tab2router: Tab2Component
}
}
]
})
@mattsteve the problem with this is that the history is shared between the two tabs—it is not possible to get away with only one router. Changing the page on the first tab will change the page on the second, which is certainly not what you want. I ended up figuring out that you can still do what I did, and nest routers. However, I ended up switching to Ionic's ion-nav
, so I get native-like swipe-to-go-back behavior on iOS. I used vue-custom-element and vue-fragment to make that work.
@lights0123 thanks for sharing your implementation, sorry for not replying earlier. Could you share your implementation using ion-nav
? I am working on the tabs functionality for the Vue 3 version and was considering a router with a stack of history states or something along those lines and I was thinking of back-porting it to the upcoming 2.0.0 version as well. I just don't want to force people into using a custom router, I'd rather have them work with something they're accustom to
@michaeltintiuc Here's the complete project. I actually removed all Ionic-Vue parts, and I'm now just using Ionic components directly from Vue because I got a bunch of errors when migrating to Ionic 5. This commit is the last commit to use this repository, on Ionic 4. Here's the main layout of my app before I switched to using my own bindings:
<template>
<ion-app>
<ion-tabs>
<ion-tab tab="news">
<ion-nav root="app-news" />
</ion-tab>
<ion-tab tab="saved">
<ion-nav root="app-saved" />
</ion-tab>
<ion-tab tab="settings">
<ion-nav root="app-settings" />
</ion-tab>
<ion-tab-bar slot="bottom" ref="tabBar">
<ion-tab-button tab="news" @click="click('news')">
<ion-icon name="paper" />
<ion-label>News</ion-label>
</ion-tab-button>
<ion-tab-button tab="saved" @click="click('saved')">
<ion-icon :src="bookmarkURL" class="bookmark-icon" />
<ion-label>Saved</ion-label>
</ion-tab-button>
<ion-tab-button tab="settings" @click="click('settings')">
<ion-icon name="settings" />
<ion-label>Settings</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-app>
</template>
/* Note: I'm implicitly letting there be more than this route, as they are automatically pushed by <ion-tabs>, and Ionic takes care of displaying different components */
/* This is the only Vue router in the entire project, everything else is done through Ionic APIs */
/* This was removed in my latest version, where I switch away from using this repo */
router: new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes: [{ path: '/', redirect: '/news' }],
}),
<template>
<!-- <fragment> seems to be needed when using this as a component, placing the header and content in a <div> broke stuff -->
<!-- Vue 3 is getting multi-component roots (I think, right?) -->
<fragment>
<ion-header>
<ion-toolbar>
<ion-title><logo /></ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
whatever
</ion-content>
</fragment>
</template>
<script lang="ts">
/* Hack to make each component be a child of the main Vue instance so things like Vuex
* are available from each component, and makes debugging via the Vue Devtools plugin
* possible
*
* Very clunky, I'd like to get rid of it but it works 🥴 (except hot-reload breaks half the time,
* but stable in production)
*/
export const injectParent = {
beforeCreateVueInstance(RootComponentDefinition) {
RootComponentDefinition.parent = window.vue;
RootComponentDefinition.store = window.vue.$store;
return RootComponentDefinition;
},
};
/* I'm using vue-class-component, and `News` is the name of my default export
* This will likely be different (namely not including `.options`) if you're using the standard
* `export default`, and I have no clue what you would do with the new Composition API
*
* Note how this matches up with the `root` attribute of each <ion-nav />
*/
Vue.customElement('app-news', (News as any).options, injectParent);
</script>
import Vue from 'vue';
import vueCustomElement from 'vue-custom-element';
import { Plugin } from 'vue-fragment';
import Vue from 'vue';
Vue.use(vueCustomElement);
This setup has a few advantages:
<ion-nav>
basically does the same thing as <keep-alive>
I understand that you probably don't want to make everyone follow these same steps, which is why I've basically migrated away from this repo (as it was getting in the way with
Thanks for the example and feedback @lights0123 I think most of this stuff makes a lot of sense and is the general direction v3 is going in. Would be interested in hearing your thoughts on the new version when it's out of alpha
You can give a try with the vue 3 version, npm i @modus/ionic-vue@next
it's not separate routers per tab as that's not quite possible with Vue or even web history as the end of the day, but the behavior is much closer to what is expected
I'm trying to test out the tabs functionality with Vue 3 and ionic-vue@next, and I'm stuck.
<template>
<IonApp>
<IonTabs>
<IonTab tab="tab1" :routes="['home']" :to="{name: 'home'}">
<IonRouterView name="tab1"/>
</IonTab>
<IonTab tab="tab2" :routes="['about']" :to="{name: 'about'}">
<IonRouterView name="tab2" />
</IonTab>
<template v-slot:bottom>
<IonTabBar>
<IonTabButton tab="tab1" :to="{name: 'home'}">
<IonLabel>Tab 1</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" :to="{name: 'about'}">
<IonLabel>Tab 2</IonLabel>
</IonTabButton>
</IonTabBar>
</template>
</IonTabs>
</IonApp>
</template>
<script>
import {
IonApp,
IonTab,
IonTabs,
IonLabel,
IonTabBar,
IonTabButton,
IonRouterView
} from "@modus/ionic-vue";
export default {
name: "App",
components: {
IonApp,
IonTab,
IonTabs,
IonLabel,
IonTabBar,
IonTabButton,
IonRouterView
},
};
</script>
I've made a setup like its described in the https://ionicframework.com/docs/api/tabs , just adapted to ionic-vue components, and I keep getting these errors.
Can you give me some working example or point me at some direction or resource so I can figure it out? 🙏
Thanks
Sure, here's my setup with a router:
<template>
<div class="ion-page">
<IonTabs @ionTabsWillChange="myMethod">
<RouterView />
<template v-slot:top>
<IonTabBar>
<IonTabButton tab="foo" href="/">
<IonIcon icon="add" />
</IonTabButton>
<IonTabButton tab="bar" href="/bar">
<IonIcon icon="map" />
</IonTabButton>
</IonTabBar>
</template>
</IonTabs>
</div>
</template>
Each component rendered by the router is similar to this:
<template>
<IonTab tab="foo">
<IonRouterView />
</IonTab>
</template>
In this example each tab has sub-routes for navigation, meaning that in order to resolve IonRouterView we'd be using the router's nested routes defined by the children
prop:
routes: [
{
path: '/',
component: foo,
children: [
{
path: '',
component: fooList,
},
{
path: 'foo/:fooId',
component: fooItem,
},
],
},
Note that we're using RouterView
within IonTabs
as we don't need any transitions and just want a route switch, within IonTab
we're using IonRouterView
to leverage transitions.
Thanks a lot. You've pointed me in the right direction with the multiple nested children routes. To be honest it seems a little odd that you have 2 levels of children routes required to have tabbed routing, but I guess it makes sense because root tab routes should not have animation.
I have one more issue, and I think it's related to tabs. ios swipe back gesture doesn't work, it just freezes, and the tab bar disappears. It's strange that I'm not seeing any console error.
I've pushed the code here so you can reproduce and give me a hand if you can
@meoweloper Please see this PR against your repo https://github.com/meoweloper/ionic-vue-3/pull/1 The tabs work, but you pointed out a type-o in the router outlet, I'm going to release a new version ASAP, thank you! Please note that swipe-back currently does not have a limit to itself, meaning that you can swipe back past the tab's root route, the fix is in the works, the router will have separate history states for each tab and one for global navigation
See version 3.0.0-alpha.14
should be available in a bit
Glad to hear that separate history is in the works, I think thats the missing piece that will push tabs to a beta state.
See version
3.0.0-alpha.14
should be available in a bit
I found that same issue component type, just didn't know how to fix it 👶 ⌨️
Are you saying you get the same error?
No, you fixed it in alpha.14. Just saying I failed to fix it myself.
It was a library issue that I introduced some time ago, it's on me :)
I'm writing a mobile app. Each tab should have its own navigation stack, i.e. you can navigate in the "News" tab, switch to the about tab and hit "About", and switch back to the News tab, leaving off right where you were, in both scroll position and path. In my app, I'm using version 1.1.2 of
@modus/ionic-vue
, as it does not require a router for tabs. The way I have this working is that each tab has its ownVueRouter
running inabstract
mode. This shouldn't be possible in other navigation modes, as going back should not change tabs. Other apps, such as the ionic-vue-conference-app, try to implement this, but fail badly. Other than being broken by using relative paths, switching between tabs doesn't retain the previous navigated path. This is a key feature in many apps, and is holding me back to this old version. See this demo of what I currently have, which is what I want. What is the proper way to do this?