77 HostListener ,
88 Output ,
99 ViewChild ,
10+ AfterViewInit ,
11+ QueryList ,
1012} from '@angular/core' ;
1113
1214import { NavController } from '../../providers/nav-controller' ;
@@ -17,14 +19,15 @@ import { StackDidChangeEvent, StackWillChangeEvent } from './stack-utils';
1719 selector : 'ion-tabs' ,
1820} )
1921// eslint-disable-next-line @angular-eslint/directive-class-suffix
20- export abstract class IonTabs implements AfterContentInit , AfterContentChecked {
22+ export abstract class IonTabs implements AfterViewInit , AfterContentInit , AfterContentChecked {
2123 /**
2224 * Note: These must be redeclared on each child class since it needs
2325 * access to generated components such as IonRouterOutlet and IonTabBar.
2426 */
2527 abstract outlet : any ;
2628 abstract tabBar : any ;
27- abstract tabBars : any ;
29+ abstract tabBars : QueryList < any > ;
30+ abstract tabs : QueryList < any > ;
2831
2932 @ViewChild ( 'tabsInner' , { read : ElementRef , static : true } ) tabsInner : ElementRef < HTMLDivElement > ;
3033
@@ -39,8 +42,29 @@ export abstract class IonTabs implements AfterContentInit, AfterContentChecked {
3942
4043 private tabBarSlot = 'bottom' ;
4144
45+ private hasTab = false ;
46+ private selectedTab ?: { tab : string } ;
47+ private leavingTab ?: any ;
48+
4249 constructor ( private navCtrl : NavController ) { }
4350
51+ ngAfterViewInit ( ) : void {
52+ /**
53+ * Developers must pass at least one ion-tab
54+ * inside of ion-tabs if they want to use a
55+ * basic tab-based navigation without the
56+ * history stack or URL updates associated
57+ * with the router.
58+ */
59+ const firstTab = this . tabs . length > 0 ? this . tabs . first : undefined ;
60+
61+ if ( firstTab ) {
62+ this . hasTab = true ;
63+ this . setActiveTab ( firstTab . tab ) ;
64+ this . tabSwitch ( ) ;
65+ }
66+ }
67+
4468 ngAfterContentInit ( ) : void {
4569 this . detectSlotChanges ( ) ;
4670 }
@@ -96,6 +120,19 @@ export abstract class IonTabs implements AfterContentInit, AfterContentChecked {
96120 select ( tabOrEvent : string | CustomEvent ) : Promise < boolean > | undefined {
97121 const isTabString = typeof tabOrEvent === 'string' ;
98122 const tab = isTabString ? tabOrEvent : ( tabOrEvent as CustomEvent ) . detail . tab ;
123+
124+ /**
125+ * If the tabs are not using the router, then
126+ * the tab switch logic is handled by the tabs
127+ * component itself.
128+ */
129+ if ( this . hasTab ) {
130+ this . setActiveTab ( tab ) ;
131+ this . tabSwitch ( ) ;
132+
133+ return ;
134+ }
135+
99136 const alreadySelected = this . outlet . getActiveStackId ( ) === tab ;
100137 const tabRootUrl = `${ this . outlet . tabsPrefix } /${ tab } ` ;
101138
@@ -142,7 +179,46 @@ export abstract class IonTabs implements AfterContentInit, AfterContentChecked {
142179 }
143180 }
144181
182+ private setActiveTab ( tab : string ) : void {
183+ const tabs = this . tabs ;
184+ const selectedTab = tabs . find ( ( t : any ) => t . tab === tab ) ;
185+
186+ if ( ! selectedTab ) {
187+ console . error ( `[Ionic Error]: Tab with id: "${ tab } " does not exist` ) ;
188+ return ;
189+ }
190+
191+ this . leavingTab = this . selectedTab ;
192+ this . selectedTab = selectedTab ;
193+
194+ this . ionTabsWillChange . emit ( { tab } ) ;
195+
196+ selectedTab . el . active = true ;
197+ }
198+
199+ private tabSwitch ( ) : void {
200+ const { selectedTab, leavingTab } = this ;
201+
202+ if ( this . tabBar && selectedTab ) {
203+ this . tabBar . selectedTab = selectedTab . tab ;
204+ }
205+
206+ if ( leavingTab ?. tab !== selectedTab ?. tab ) {
207+ if ( leavingTab ?. el ) {
208+ leavingTab . el . active = false ;
209+ }
210+ }
211+
212+ if ( selectedTab ) {
213+ this . ionTabsDidChange . emit ( { tab : selectedTab . tab } ) ;
214+ }
215+ }
216+
145217 getSelected ( ) : string | undefined {
218+ if ( this . hasTab ) {
219+ return this . selectedTab ?. tab ;
220+ }
221+
146222 return this . outlet . getActiveStackId ( ) ;
147223 }
148224
0 commit comments