Skip to content
1 change: 1 addition & 0 deletions packages/angular/standalone/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { IonModal } from './overlays/modal';
export { IonPopover } from './overlays/popover';
export { IonRouterOutlet } from './navigation/router-outlet';
export { IonRouterLink, IonRouterLinkWithHref } from './navigation/router-link-delegate';
export { IonTabs } from './navigation/tabs';
export { provideIonicAngular } from './providers/ionic-angular';
export {
ActionSheetController,
Expand Down
52 changes: 52 additions & 0 deletions packages/angular/standalone/src/navigation/tabs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Component, ContentChild, ContentChildren, ViewChild, QueryList } from '@angular/core';
import { IonTabs as IonTabsBase } from '@ionic/angular/common';

import { IonTabBar } from '../directives/proxies';

import { IonRouterOutlet } from './router-outlet';

@Component({
selector: 'ion-tabs',
template: `
<ng-content select="[slot=top]"></ng-content>
<div class="tabs-inner" #tabsInner>
<ion-router-outlet #outlet tabs="true" (stackEvents)="onPageSelected($event)"></ion-router-outlet>
</div>
<ng-content></ng-content>
`,
standalone: true,
styles: [
`
:host {
display: flex;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;

flex-direction: column;

width: 100%;
height: 100%;

contain: layout size style;
}
.tabs-inner {
position: relative;

flex: 1;

contain: layout size style;
}
`,
],
imports: [IonRouterOutlet],
})
// eslint-disable-next-line @angular-eslint/component-class-suffix
export class IonTabs extends IonTabsBase {
@ViewChild('outlet', { read: IonRouterOutlet, static: false }) outlet: IonRouterOutlet;

@ContentChild(IonTabBar, { static: false }) tabBar: IonTabBar | undefined;
@ContentChildren(IonTabBar) tabBars: QueryList<IonTabBar>;
}
16 changes: 16 additions & 0 deletions packages/angular/test/base/e2e/src/standalone/tabs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
describe('Tabs', () => {
beforeEach(() => {
cy.visit('/standalone/tabs');
});

it('should redirect to the default tab', () => {
cy.get('app-tab-one').should('be.visible');
cy.contains('Tab 1');
});

it('should render new content when switching tabs', () => {
cy.get('#tab-button-tab-two').click();
cy.get('app-tab-two').should('be.visible');
cy.contains('Tab 2');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ export const routes: Routes = [
{ path: 'overlay-controllers', loadComponent: () => import('../overlay-controllers/overlay-controllers.component').then(c => c.OverlayControllersComponent) },
{ path: 'button', loadComponent: () => import('../button/button.component').then(c => c.ButtonComponent) },
{ path: 'icon', loadComponent: () => import('../icon/icon.component').then(c => c.IconComponent) },
{ path: 'tabs', redirectTo: '/standalone/tabs/tab-one', pathMatch: 'full' },
{
path: 'tabs',
loadComponent: () => import('../tabs/tabs.component').then(c => c.TabsComponent),
children: [
{ path: 'tab-one', loadComponent: () => import('../tabs/tab1.component').then(c => c.TabOneComponent) },
{ path: 'tab-two', loadComponent: () => import('../tabs/tab2.component').then(c => c.TabTwoComponent) },
{ path: 'tab-three', loadComponent: () => import('../tabs/tab3.component').then(c => c.TabThreeComponent) }
]
},
]
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component } from '@angular/core';

@Component({
selector: 'app-tab-one',
template: `
Tab 1
`,
standalone: true,
})
export class TabOneComponent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component } from '@angular/core';

@Component({
selector: 'app-tab-two',
template: `
Tab 2
`,
standalone: true,
})
export class TabTwoComponent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component } from '@angular/core';

@Component({
selector: 'app-tab-three',
template: `
Tab 3
`,
standalone: true,
})
export class TabThreeComponent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<ion-tabs (ionTabsDidChange)="tabChanged($event)" (ionTabsWillChange)="tabsWillChange($event)">
<ion-tab-bar>
<ion-tab-button tab="tab-one">
<ion-label>Tab One</ion-label>
<ion-icon name="add"></ion-icon>
</ion-tab-button>

<ion-tab-button tab="tab-two">
<ion-label>Tab Two</ion-label>
<ion-icon name="logo-ionic"></ion-icon>
</ion-tab-button>

<ion-tab-button tab="tab-three">
<ion-label>Tab Three</ion-label>
<ion-icon name="save"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
<div id="test">
<ul>
<li>
ionTabsWillChange counter: <span id="ionTabsWillChangeCounter">{{ tabsWillChangeCounter }}</span>
</li>
<li>
ionTabsWillChange event: <span id="ionTabsWillChangeEvent">{{ tabsWillChangeEvent }}</span>
</li>
<li>
ionTabsWillChange selectedTab: <span id="ionTabsWillChangeSelectedTab">{{ tabsWillChangeSelectedTab }}</span>
</li>
</ul>
<ul>
<li>
ionTabsDidChange counter: <span id="ionTabsDidChangeCounter">{{ tabsDidChangeCounter }}</span>
</li>
<li>
ionTabsDidChange event: <span id="ionTabsDidChangeEvent">{{ tabsDidChangeEvent }}</span>
</li>
<li>
ionTabsDidChange selectedTab: <span id="ionTabsDidChangeSelectedTab">{{ tabsDidChangeSelectedTab }}</span>
</li>
</ul>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Component, ViewChild } from '@angular/core';
import { IonTabBar, IonTabButton, IonIcon, IonLabel, IonTabs } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { add, logoIonic, save } from 'ionicons/icons';

addIcons({ add, logoIonic, save });

@Component({
selector: 'app-tabs',
templateUrl: './tabs.component.html',
standalone: true,
imports: [IonTabBar, IonTabButton, IonIcon, IonLabel, IonTabs]
})
export class TabsComponent {
tabsDidChangeCounter = 0;
tabsDidChangeEvent = '';
tabsDidChangeSelectedTab? = '';

tabsWillChangeCounter = 0;
tabsWillChangeEvent = '';
tabsWillChangeSelectedTab? = '';

@ViewChild(IonTabBar) tabBar!: IonTabBar;

tabChanged(ev: { tab: string }) {
console.log('ionTabsDidChange', this.tabBar.selectedTab);
this.tabsDidChangeCounter++;
this.tabsDidChangeEvent = ev.tab;
this.tabsDidChangeSelectedTab = this.tabBar.selectedTab;
}

tabsWillChange(ev: { tab: string }) {
console.log('ionTabsWillChange', this.tabBar.selectedTab);
this.tabsWillChangeCounter++;
this.tabsWillChangeEvent = ev.tab;
this.tabsWillChangeSelectedTab = this.tabBar.selectedTab;
}
}