bug: angular: navigations that overlap a page transition can leave two pages visible

未关闭
#31,650 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
35/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
领域
frontend

调研方向

Start in common/directives/navigation/stack-controller.ts, reading StackController.wait() and how runningTask is set and cleared; the issue already suggests tasks can resume concurrently via promise.finally(). Reproduce with the provided zoneless Angular app and the 8-navigation burst driver, checking visible .ion-page elements after settling. Done means overlapping navigations always settle with exactly one visible page matching the current URL, verified across repeated bursts.

由索引模型根据 Issue 内容生成。

描述

package: angular type: bug
Ionic Framework Version

v9.x (9.0.9)

Current Behavior

When router navigations follow each other faster than the page transition — a navigation starts while the previous transition is still animating — the outlet sometimes settles with two .ion-page elements displayed: the page for the current URL and a page from an earlier navigation.

In a headless Chromium run of the reproduction below (iOS mode, default transitions), 5 of 40 bursts of 8 navigations, 30–330 ms apart, ended with two pages visible.

From reading StackController.wait() (common/directives/navigation/stack-controller.ts): every task waiting on runningTask resumes at the same moment once it settles, and promise.finally(() => (this.runningTask = undefined)) can clear the slot while a later task is still running, so queued transitions can run at the same time rather than one after another. That does not seem to be the only path: with wait() changed locally to chain tasks strictly, 1 of 40 bursts still failed.

Expected Behavior

Once navigations settle, exactly one page is visible: the one for the current URL.

Steps to Reproduce
  1. A standalone Angular app with provideIonicAngular({ mode: 'ios' }), an <ion-router-outlet> and three sibling routes a, b, c, each a component with an <ion-content>.
  2. Call router.navigateByUrl() 8 times in a row to random routes among the three, 30–330 ms apart.
  3. Wait ~3 s and count the ion-router-outlet > .ion-page elements that are displayed with non-zero opacity.
const page = (name: string) => {
  @Component({ selector: `page-${name}`, template: `<ion-content>${name}</ion-content>`, imports: [IonContent] })
  class P {}
  return P;
};

@Component({ selector: 'app-root', template: '<ion-app><ion-router-outlet /></ion-app>', imports: [IonApp, IonRouterOutlet] })
class Root {}

bootstrapApplication(Root, {
  providers: [
    provideZonelessChangeDetection(),
    provideIonicAngular({ mode: 'ios' }),
    provideRouter([
      { path: 'a', component: page('a') },
      { path: 'b', component: page('b') },
      { path: 'c', component: page('c') },
      { path: '', redirectTo: 'a', pathMatch: 'full' },
    ]),
  ],
});

// Driver: navigate 8 times to random routes, 30–330 ms apart, then after ~3 s:
const visible = [...document.querySelectorAll('ion-router-outlet > .ion-page')]
  .filter((p) => getComputedStyle(p).display !== 'none' && +getComputedStyle(p).opacity > 0);
// Expected visible.length === 1; observed 2 in about 1 of 8 bursts.
Code Reproduction URL

Inline above.

Ionic Info

@ionic/angular 9.0.9, @ionic/core 9.0.9, @angular/core 22.2.3 (zoneless), Chromium via Playwright.

Additional Information

In our app this showed up as tab switches settling on a page that was not the tab in the URL after fast taps.

主要语言
TypeScript
星标
52.7k
派生
13.3k
平均合并
3 天 4 小时
30 天内合并 PR
72

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

ionic-team/ionic-framework 的其他 Issue

查看 ionic-team/ionic-framework 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。