refactor(vue): remove auto-generated router outlet inside of ion-tabs (#23479)

This commit is contained in:
Liam DeBeasi
2021-06-21 18:22:57 -04:00
committed by GitHub
parent c2eda6aee3
commit 6dfbd89cf8
8 changed files with 72 additions and 103 deletions

View File

@ -27,6 +27,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
* [Config Provider](#config-provider) * [Config Provider](#config-provider)
- [Vue](#vue) - [Vue](#vue)
* [Tabs Config](#tabs-config) * [Tabs Config](#tabs-config)
* [Tabs Router Outlet](#tabs-router-outlet)
* [Overlay Events](#overlay-events) * [Overlay Events](#overlay-events)
- [Browser and Platform Support](#browser-and-platform-support) - [Browser and Platform Support](#browser-and-platform-support)
@ -220,6 +221,47 @@ const routes: Array<RouteRecordRaw> = [
In the example above `tabs/tab1/view` has been rewritten has a sibling route to `tabs/tab1`. The `path` field now includes the `tab1` prefix. In the example above `tabs/tab1/view` has been rewritten has a sibling route to `tabs/tab1`. The `path` field now includes the `tab1` prefix.
#### Tabs Router Outlet
Developers must now provide an `ion-router-outlet` inside of `ion-tabs`. Previously one was generated automatically, but this made it difficult for developers to access the properties on the generated `ion-router-outlet`.
**Old**
```html
<ion-tabs>
<ion-tab-bar slot="bottom">
...
</ion-tab-bar>
</ion-tabs>
<script>
import { IonTabs, IonTabBar } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: { IonTabs, IonTabBar }
});
</script>
```
**New**
```html
<ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom">
...
</ion-tab-bar>
</ion-tabs>
<script>
import { IonTabs, IonTabBar, IonRouterOutlet } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: { IonTabs, IonTabBar, IonRouterOutlet }
});
</script>
```
#### Overlay Events #### Overlay Events
Overlay events `onWillPresent`, `onDidPresent`, `onWillDismiss`, and `onDidDismiss` have been removed in favor of `willPresent`, `didPresent`, `willDismiss`, and `didDismiss`. Overlay events `onWillPresent`, `onDidPresent`, `onWillDismiss`, and `onDidDismiss` have been removed in favor of `willPresent`, `didPresent`, `willDismiss`, and `didDismiss`.

View File

@ -1,5 +1,4 @@
import { h, defineComponent, VNode } from 'vue'; import { h, defineComponent, VNode } from 'vue';
import { IonRouterOutlet } from './IonRouterOutlet';
const WILL_CHANGE = 'ionTabsWillChange'; const WILL_CHANGE = 'ionTabsWillChange';
const DID_CHANGE = 'ionTabsDidChange'; const DID_CHANGE = 'ionTabsDidChange';
@ -7,20 +6,21 @@ const DID_CHANGE = 'ionTabsDidChange';
export const IonTabs = /*@__PURE__*/ defineComponent({ export const IonTabs = /*@__PURE__*/ defineComponent({
name: 'IonTabs', name: 'IonTabs',
emits: [WILL_CHANGE, DID_CHANGE], emits: [WILL_CHANGE, DID_CHANGE],
data() {
return { didWarn: false }
},
render() { render() {
const { $slots: slots, $emit, $data } = this; const { $slots: slots, $emit } = this;
const slottedContent = slots.default && slots.default(); const slottedContent = slots.default && slots.default();
let userProvidedRouterOutlet; let routerOutlet;
if (slottedContent && slottedContent.length > 0) {
/** /**
* If developer passed in their own ion-router-outlet * Developers must pass an ion-router-outlet
* instance, then we should not init a default one * inside of ion-tabs.
*/ */
userProvidedRouterOutlet = slottedContent.find((child: VNode) => child.type && (child.type as any).name === 'IonRouterOutlet'); if (slottedContent && slottedContent.length > 0) {
routerOutlet = slottedContent.find((child: VNode) => child.type && (child.type as any).name === 'IonRouterOutlet');
}
if (!routerOutlet) {
throw new Error('IonTabs must contain an IonRouterOutlet. See https://ionicframework.com/docs/vue/navigation#working-with-tabs for more information.');
} }
let childrenToRender = [ let childrenToRender = [
@ -31,42 +31,15 @@ export const IonTabs = /*@__PURE__*/ defineComponent({
'flex': '1', 'flex': '1',
'contain': 'layout size style' 'contain': 'layout size style'
} }
}, (userProvidedRouterOutlet) ? userProvidedRouterOutlet : [h(IonRouterOutlet)]) }, routerOutlet)
]; ];
if (userProvidedRouterOutlet && !$data.didWarn) {
console.warn(`[@ionic/vue Deprecation] Starting in Ionic Vue v6.0, developers must add an 'ion-router-outlet' instance inside of 'ion-tabs'.
Before:
<ion-tabs>
<ion-tab-bar slot="bottom">
...
</ion-tab-bar>
</ion-tabs>
After:
<ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom">
...
</ion-tab-bar>
</ion-tabs>
Be sure to import 'IonRouterOutlet' from '@ionic/vue' and provide that import to your Vue component. See https://ionicframework.com/docs/vue/navigation#working-with-tabs for more information.
`);
$data.didWarn = true;
}
/** /**
* If ion-tab-bar has slot="top" it needs to be * If ion-tab-bar has slot="top" it needs to be
* rendered before `.tabs-inner` otherwise it will * rendered before `.tabs-inner` otherwise it will
* not show above the tab content. * not show above the tab content.
*/ */
if (slottedContent && slottedContent.length > 0) { if (slottedContent && slottedContent.length > 0) {
/** /**
* Render all content except for router outlet * Render all content except for router outlet
* since that needs to be inside of `.tabs-inner`. * since that needs to be inside of `.tabs-inner`.

View File

@ -2,6 +2,7 @@
<ion-page data-pageid="tabs"> <ion-page data-pageid="tabs">
<ion-content> <ion-content>
<ion-tabs id="tabs"> <ion-tabs id="tabs">
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom"> <ion-tab-bar slot="bottom">
<ion-tab-button <ion-tab-button
v-for="tab in tabs" v-for="tab in tabs"
@ -21,14 +22,14 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { IonButton, IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage } from '@ionic/vue'; import { IonButton, IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage, IonRouterOutlet } from '@ionic/vue';
import { ellipse, square, triangle, shield } from 'ionicons/icons'; import { ellipse, square, triangle, shield } from 'ionicons/icons';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { ref, defineComponent } from 'vue'; import { ref, defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: 'Tabs', name: 'Tabs',
components: { IonButton, IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage }, components: { IonButton, IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage, IonRouterOutlet },
setup() { setup() {
const tabs = ref([ const tabs = ref([
{ id: 1, icon: triangle }, { id: 1, icon: triangle },

View File

@ -1,54 +0,0 @@
<template>
<ion-page data-pageid="tabs">
<ion-content>
<ion-tabs id="tabs">
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom">
<ion-tab-button
v-for="tab in tabs"
:tab="'tab' + tab.id"
:href="'/tabs-new/tab' + tab.id"
:key="tab.id"
>
<ion-icon :icon="tab.icon" />
<ion-label>Tab {{ tab.id }}</ion-label>
</ion-tab-button>
<ion-button id="add-tab" @click="addTab()">Add Tab</ion-button>
</ion-tab-bar>
</ion-tabs>
</ion-content>
</ion-page>
</template>
<script lang="ts">
import { IonButton, IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage, IonRouterOutlet } from '@ionic/vue';
import { ellipse, square, triangle, shield } from 'ionicons/icons';
import { useRouter } from 'vue-router';
import { ref, defineComponent } from 'vue';
export default defineComponent({
name: 'Tabs',
components: { IonButton, IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage, IonRouterOutlet },
setup() {
const tabs = ref([
{ id: 1, icon: triangle },
{ id: 2, icon: ellipse },
{ id: 3, icon: square }
])
const router = useRouter();
const addTab = () => {
router.addRoute({ path: '/tabs/tab4', component: () => import('@/views/Tab4.vue') });
tabs.value = [
...tabs.value,
{
id: 4,
icon: shield
}
]
}
return { tabs, addTab }
}
});
</script>

View File

@ -2,6 +2,7 @@
<ion-page data-pageid="tabs-secondary"> <ion-page data-pageid="tabs-secondary">
<ion-content> <ion-content>
<ion-tabs id="tabs"> <ion-tabs id="tabs">
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="top"> <ion-tab-bar slot="top">
<ion-tab-button tab="tab1-secondary" href="/tabs-secondary/tab1"> <ion-tab-button tab="tab1-secondary" href="/tabs-secondary/tab1">
<ion-icon :icon="triangle" /> <ion-icon :icon="triangle" />
@ -24,12 +25,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage } from '@ionic/vue'; import { IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage, IonRouterOutlet } from '@ionic/vue';
import { ellipse, square, triangle } from 'ionicons/icons'; import { ellipse, square, triangle } from 'ionicons/icons';
export default { export default {
name: 'Tabs', name: 'Tabs',
components: { IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage }, components: { IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage, IonRouterOutlet },
setup() { setup() {
return { return {
ellipse, ellipse,

View File

@ -191,10 +191,11 @@ describe('Routing', () => {
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/22492 // Verifies fix for https://github.com/ionic-team/ionic-framework/issues/22492
it('should show correct view when replacing', async () => { it('should show correct view when replacing', async () => {
const Tabs = { const Tabs = {
components: { IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel }, components: { IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel, IonRouterOutlet },
template: ` template: `
<ion-page> <ion-page>
<ion-tabs> <ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="top"> <ion-tab-bar slot="top">
<ion-tab-button tab="tab1" href="/tabs/tab1"> <ion-tab-button tab="tab1" href="/tabs/tab1">
<ion-label>Tab 1</ion-label> <ion-label>Tab 1</ion-label>

View File

@ -10,10 +10,11 @@ const App = {
describe('ion-tab-bar', () => { describe('ion-tab-bar', () => {
it('should render in the top slot', async () => { it('should render in the top slot', async () => {
const Tabs = { const Tabs = {
components: { IonPage, IonTabs, IonTabBar }, components: { IonPage, IonTabs, IonTabBar, IonRouterOutlet },
template: ` template: `
<ion-page> <ion-page>
<ion-tabs> <ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="top"></ion-tab-bar> <ion-tab-bar slot="top"></ion-tab-bar>
</ion-tabs> </ion-tabs>
</ion-page> </ion-page>
@ -42,10 +43,11 @@ describe('ion-tab-bar', () => {
it('should render in the bottom slot', async () => { it('should render in the bottom slot', async () => {
const Tabs = { const Tabs = {
components: { IonPage, IonTabs, IonTabBar }, components: { IonPage, IonTabs, IonTabBar, IonRouterOutlet },
template: ` template: `
<ion-page> <ion-page>
<ion-tabs> <ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom"></ion-tab-bar> <ion-tab-bar slot="bottom"></ion-tab-bar>
</ion-tabs> </ion-tabs>
</ion-page> </ion-page>
@ -74,10 +76,11 @@ describe('ion-tab-bar', () => {
it('should render in the default slot', async () => { it('should render in the default slot', async () => {
const Tabs = { const Tabs = {
components: { IonPage, IonTabs, IonTabBar }, components: { IonPage, IonTabs, IonTabBar, IonRouterOutlet },
template: ` template: `
<ion-page> <ion-page>
<ion-tabs> <ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar></ion-tab-bar> <ion-tab-bar></ion-tab-bar>
</ion-tabs> </ion-tabs>
</ion-page> </ion-page>
@ -106,10 +109,11 @@ describe('ion-tab-bar', () => {
// Verifies the fix for https://github.com/ionic-team/ionic-framework/issues/22642 // Verifies the fix for https://github.com/ionic-team/ionic-framework/issues/22642
it('should not fail on non tab button elements', async () => { it('should not fail on non tab button elements', async () => {
const Tabs = { const Tabs = {
components: { IonPage, IonTabs, IonTabBar }, components: { IonPage, IonTabs, IonTabBar, IonRouterOutlet },
template: ` template: `
<ion-page> <ion-page>
<ion-tabs> <ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar> <ion-tab-bar>
<!-- my comment --> <!-- my comment -->
</ion-tab-bar> </ion-tab-bar>

View File

@ -9,10 +9,11 @@ const App = {
} }
const Tabs = { const Tabs = {
components: { IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel }, components: { IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel, IonRouterOutlet },
template: ` template: `
<ion-page> <ion-page>
<ion-tabs> <ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="top"> <ion-tab-bar slot="top">
<ion-tab-button tab="tab1" href="/tab1"> <ion-tab-button tab="tab1" href="/tab1">
<ion-label>Tab 1</ion-label> <ion-label>Tab 1</ion-label>