fix(angular): ionTabsWillChange is fired before tab activation (#27991)

Issue number: Resolves #27212

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

`ionTabsWillChange` emits _after_ the tab view is activated in the
stack.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- `ionTabsWillChange` emits _before_ the tab view is activated in the
stack.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev-build: `7.2.4-dev.11692040948.1fd0ecd2`
This commit is contained in:
Sean Perkins
2023-08-18 11:07:09 -04:00
committed by GitHub
parent 1015c06cbe
commit bbfb8f81a6
4 changed files with 62 additions and 11 deletions

View File

@ -30,7 +30,7 @@ import { Config } from '../../providers/config';
import { NavController } from '../../providers/nav-controller';
import { StackController } from './stack-controller';
import { RouteView, getUrl } from './stack-utils';
import { RouteView, StackDidChangeEvent, StackWillChangeEvent, getUrl, isTabSwitch } from './stack-utils';
// TODO(FW-2827): types
@ -66,7 +66,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
*/
@Input() name = PRIMARY_OUTLET;
@Output() stackEvents = new EventEmitter<any>();
/** @internal */
@Output() stackWillChange = new EventEmitter<StackWillChangeEvent>();
/** @internal */
@Output() stackDidChange = new EventEmitter<StackDidChangeEvent>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@Output('activate') activateEvents = new EventEmitter<any>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@ -304,9 +308,16 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
*/
this.navCtrl.setTopOutlet(this);
const leavingView = this.stackCtrl.getActiveView();
this.stackWillChange.emit({
enteringView,
tabSwitch: isTabSwitch(enteringView, leavingView),
});
this.stackCtrl.setActive(enteringView).then((data) => {
this.activateEvents.emit(cmpRef.instance);
this.stackEvents.emit(data);
this.stackDidChange.emit(data);
});
}

View File

@ -16,14 +16,19 @@ import { NavController } from '../../providers/nav-controller';
import { IonTabBar } from '../proxies';
import { IonRouterOutlet } from './ion-router-outlet';
import { StackEvent } from './stack-utils';
import { StackDidChangeEvent, StackWillChangeEvent } from './stack-utils';
@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>
<ion-router-outlet
#outlet
tabs="true"
(stackWillChange)="onStackWillChange($event)"
(stackDidChange)="onStackDidChange($event)"
></ion-router-outlet>
</div>
<ng-content></ng-content>
`,
@ -62,7 +67,13 @@ export class IonTabs implements AfterContentInit, AfterContentChecked {
@ContentChild(IonTabBar, { static: false }) tabBar: IonTabBar | undefined;
@ContentChildren(IonTabBar) tabBars: QueryList<IonTabBar>;
/**
* Emitted before the tab view is changed.
*/
@Output() ionTabsWillChange = new EventEmitter<{ tab: string }>();
/**
* Emitted after the tab view is changed.
*/
@Output() ionTabsDidChange = new EventEmitter<{ tab: string }>();
private tabBarSlot = 'bottom';
@ -80,10 +91,19 @@ export class IonTabs implements AfterContentInit, AfterContentChecked {
/**
* @internal
*/
onPageSelected(detail: StackEvent): void {
const stackId = detail.enteringView.stackId;
if (detail.tabSwitch && stackId !== undefined) {
onStackWillChange({ enteringView, tabSwitch }: StackWillChangeEvent): void {
const stackId = enteringView.stackId;
if (tabSwitch && stackId !== undefined) {
this.ionTabsWillChange.emit({ tab: stackId });
}
}
/**
* @internal
*/
onStackDidChange({ enteringView, tabSwitch }: StackDidChangeEvent): void {
const stackId = enteringView.stackId;
if (tabSwitch && stackId !== undefined) {
if (this.tabBar) {
this.tabBar.selectedTab = stackId;
}

View File

@ -8,7 +8,7 @@ import { NavController } from '../../providers/nav-controller';
import {
RouteView,
StackEvent,
StackDidChangeEvent,
computeStackId,
destroyView,
getUrl,
@ -61,7 +61,7 @@ export class StackController {
return view;
}
setActive(enteringView: RouteView): Promise<StackEvent> {
setActive(enteringView: RouteView): Promise<StackDidChangeEvent> {
const consumeResult = this.navCtrl.consumeTransition();
let { direction, animation, animationBuilder } = consumeResult;
const leavingView = this.activeView;
@ -224,6 +224,13 @@ export class StackController {
return this.activeView ? this.activeView.stackId : undefined;
}
/**
* @internal
*/
getActiveView(): RouteView | undefined {
return this.activeView;
}
hasRunningTask(): boolean {
return this.runningTask !== undefined;
}

View File

@ -79,10 +79,23 @@ export const destroyView = (view: RouteView | undefined): void => {
}
};
export interface StackEvent {
export interface StackWillChangeEvent {
enteringView: RouteView;
/**
* `true` if the event is trigged as a result of a switch
* between tab navigation stacks.
*/
tabSwitch: boolean;
}
export interface StackDidChangeEvent {
enteringView: RouteView;
direction: RouterDirection;
animation: NavDirection | undefined;
/**
* `true` if the event is trigged as a result of a switch
* between tab navigation stacks.
*/
tabSwitch: boolean;
}