diff --git a/angular/src/directives/navigation/stack-controller.ts b/angular/src/directives/navigation/stack-controller.ts index 6349f89db5..331044f47d 100644 --- a/angular/src/directives/navigation/stack-controller.ts +++ b/angular/src/directives/navigation/stack-controller.ts @@ -9,9 +9,8 @@ import { RouteView, StackEvent, computeStackId, destroyView, getUrl, insertView, export class StackController { - private viewsSnapshot: RouteView[] = []; private views: RouteView[] = []; - private runningTransition?: Promise; + private runningTask?: Promise; private skipTransition = false; private tabsPrefix: string[] | undefined; private activeView: RouteView | undefined; @@ -50,7 +49,7 @@ export class StackController { return view; } - async setActive(enteringView: RouteView): Promise { + setActive(enteringView: RouteView): Promise { let { direction, animation } = this.navCtrl.consumeTransition(); const leavingView = this.activeView; const tabSwitch = isTabSwitch(enteringView, leavingView); @@ -58,15 +57,18 @@ export class StackController { direction = 'back'; animation = undefined; } - this.insertView(enteringView, direction); - await this.transition(enteringView, leavingView, animation, this.canGoBack(1), false); - await this.cleanupAsync(); - return { - enteringView, - direction, - animation, - tabSwitch - }; + const viewsSnapshot = this.views.slice(); + const views = this.insertView(enteringView, direction); + return this.wait(async () => { + await this.transition(enteringView, leavingView, animation, this.canGoBack(1), false); + await cleanupAsync(enteringView, views, viewsSnapshot); + return { + enteringView, + direction, + animation, + tabSwitch + }; + }); } canGoBack(deep: number, stackId = this.getActiveStackId()): boolean { @@ -84,19 +86,21 @@ export class StackController { }); } - startBackTransition() { + async startBackTransition() { const leavingView = this.activeView; if (leavingView) { const views = this.getStack(leavingView.stackId); const enteringView = views[views.length - 2]; enteringView.ref.changeDetectorRef.reattach(); - this.transition( - enteringView, // entering view - leavingView, // leaving view - 'back', - true, - true - ); + await this.wait(() => { + return this.transition( + enteringView, // entering view + leavingView, // leaving view + 'back', + true, + true + ); + }); } } @@ -130,33 +134,7 @@ export class StackController { private insertView(enteringView: RouteView, direction: RouterDirection) { this.activeView = enteringView; this.views = insertView(this.views, enteringView, direction); - } - - private cleanupAsync() { - return new Promise(resolve => { - requestAnimationFrame(() => { - this.cleanup(); - resolve(); - }); - }); - } - - private cleanup() { - const activeRoute = this.activeView; - const views = this.views; - this.viewsSnapshot - .filter(view => !views.includes(view)) - .forEach(view => destroyView(view)); - - views.forEach(view => { - if (view !== activeRoute) { - const element = view.element; - element.setAttribute('aria-hidden', 'true'); - element.classList.add('ion-page-hidden'); - view.ref.changeDetectorRef.detach(); - } - }); - this.viewsSnapshot = views.slice(); + return this.views.slice(); } private async transition( @@ -166,10 +144,6 @@ export class StackController { showGoBack: boolean, progressAnimation: boolean ) { - if (this.runningTransition !== undefined) { - await this.runningTransition; - this.runningTransition = undefined; - } if (this.skipTransition) { this.skipTransition = false; return; @@ -184,14 +158,46 @@ export class StackController { } await containerEl.componentOnReady(); - this.runningTransition = containerEl.commit(enteringEl, leavingEl, { + await containerEl.commit(enteringEl, leavingEl, { deepWait: true, duration: direction === undefined ? 0 : undefined, direction, showGoBack, progressAnimation }); - await this.runningTransition; } } + + private async wait(task: () => Promise): Promise { + if (this.runningTask !== undefined) { + await this.runningTask; + this.runningTask = undefined; + } + const promise = this.runningTask = task(); + return promise; + } +} + +function cleanupAsync(activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[]) { + return new Promise(resolve => { + requestAnimationFrame(() => { + cleanup(activeRoute, views, viewsSnapshot); + resolve(); + }); + }); +} + +function cleanup(activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[]) { + viewsSnapshot + .filter(view => !views.includes(view)) + .forEach(destroyView); + + views.forEach(view => { + if (view !== activeRoute) { + const element = view.element; + element.setAttribute('aria-hidden', 'true'); + element.classList.add('ion-page-hidden'); + view.ref.changeDetectorRef.detach(); + } + }); } diff --git a/angular/test/test-app/e2e/src/form.e2e-spec.ts b/angular/test/test-app/e2e/src/form.e2e-spec.ts index 7e73900972..7fabceb136 100644 --- a/angular/test/test-app/e2e/src/form.e2e-spec.ts +++ b/angular/test/test-app/e2e/src/form.e2e-spec.ts @@ -45,7 +45,7 @@ describe('form', () => { }); it('ion-toggle should change', async () => { - await element(by.css('ion-toggle')).click(); + await element(by.css('form ion-toggle')).click(); await testData({ 'datetime': '2010-08-20', 'select': null, @@ -84,7 +84,7 @@ describe('form', () => { }); it('ion-toggle should change only after blur', async () => { - await element(by.css('ion-toggle')).click(); + await element(by.css('form ion-toggle')).click(); await testData({ 'datetime': '2010-08-20', 'select': null, diff --git a/angular/test/test-app/e2e/src/navigation.e2e-spec.ts b/angular/test/test-app/e2e/src/navigation.e2e-spec.ts new file mode 100644 index 0000000000..668e73065f --- /dev/null +++ b/angular/test/test-app/e2e/src/navigation.e2e-spec.ts @@ -0,0 +1,24 @@ +import { browser, element, by } from 'protractor'; +import { handleErrorMessages, waitTime, testStack } from './utils'; + +describe('navigation', () => { + + afterEach(() => { + handleErrorMessages(); + }); + + it('should navigate correctly', async () => { + await browser.get('/navigation/page1'); + await waitTime(2000); + await testStack('ion-router-outlet', ['app-navigation-page2', 'app-navigation-page1']); + + const pageHidden = element(by.css('app-navigation-page2')); + expect(await pageHidden.getAttribute('aria-hidden')).toEqual('true'); + expect(await pageHidden.getAttribute('class')).toEqual('ion-page ion-page-hidden'); + + const pageVisible = element(by.css('app-navigation-page1')); + expect(await pageVisible.getAttribute('aria-hidden')).toEqual(null); + expect(await pageVisible.getAttribute('class')).toEqual('ion-page can-go-back'); + }); + +}); diff --git a/angular/test/test-app/src/app/app-routing.module.ts b/angular/test/test-app/src/app/app-routing.module.ts index ea4839d5d0..d6ffbb92c9 100644 --- a/angular/test/test-app/src/app/app-routing.module.ts +++ b/angular/test/test-app/src/app/app-routing.module.ts @@ -18,6 +18,9 @@ import { ViewChildComponent } from './view-child/view-child.component'; import { ProvidersComponent } from './providers/providers.component'; import { SlidesComponent } from './slides/slides.component'; import { FormComponent } from './form/form.component'; +import { NavigationPage1Component } from './navigation-page1/navigation-page1.component'; +import { NavigationPage2Component } from './navigation-page2/navigation-page2.component'; +import { NavigationPage3Component } from './navigation-page3/navigation-page3.component'; const routes: Routes = [ { path: '', component: HomePageComponent }, @@ -32,6 +35,14 @@ const routes: Routes = [ { path: 'virtual-scroll', component: VirtualScrollComponent }, { path: 'virtual-scroll-detail/:itemId', component: VirtualScrollDetailComponent }, { path: 'tabs', redirectTo: '/tabs/account', pathMatch: 'full' }, + { + path: 'navigation', + children: [ + { path: 'page1', component: NavigationPage1Component }, + { path: 'page2', component: NavigationPage2Component }, + { path: 'page3', component: NavigationPage3Component } + ] + }, { path: 'tabs', component: TabsComponent, diff --git a/angular/test/test-app/src/app/app.module.ts b/angular/test/test-app/src/app/app.module.ts index 38f4f1decd..efad253e37 100644 --- a/angular/test/test-app/src/app/app.module.ts +++ b/angular/test/test-app/src/app/app.module.ts @@ -27,6 +27,9 @@ import { ViewChildComponent } from './view-child/view-child.component'; import { ProvidersComponent } from './providers/providers.component'; import { SlidesComponent } from './slides/slides.component'; import { FormComponent } from './form/form.component'; +import { NavigationPage1Component } from './navigation-page1/navigation-page1.component'; +import { NavigationPage2Component } from './navigation-page2/navigation-page2.component'; +import { NavigationPage3Component } from './navigation-page3/navigation-page3.component'; @NgModule({ declarations: [ @@ -51,7 +54,10 @@ import { FormComponent } from './form/form.component'; ViewChildComponent, ProvidersComponent, SlidesComponent, - FormComponent + FormComponent, + NavigationPage1Component, + NavigationPage2Component, + NavigationPage3Component ], imports: [ BrowserModule, diff --git a/angular/test/test-app/src/app/navigation-page1/navigation-page1.component.html b/angular/test/test-app/src/app/navigation-page1/navigation-page1.component.html new file mode 100644 index 0000000000..a5f940b69d --- /dev/null +++ b/angular/test/test-app/src/app/navigation-page1/navigation-page1.component.html @@ -0,0 +1,3 @@ +

+ navigation-page1 works! +

diff --git a/angular/test/test-app/src/app/navigation-page1/navigation-page1.component.ts b/angular/test/test-app/src/app/navigation-page1/navigation-page1.component.ts new file mode 100644 index 0000000000..da3d718495 --- /dev/null +++ b/angular/test/test-app/src/app/navigation-page1/navigation-page1.component.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core'; +import { NavController } from '@ionic/angular'; + +let count = 0; +@Component({ + selector: 'app-navigation-page1', + templateUrl: './navigation-page1.component.html', +}) +export class NavigationPage1Component { + constructor( + private navController: NavController + ) {} + + ionViewDidEnter() { + if (count < 1) { + this.navController.navigateBack('/navigation/page2'); + } + count++; + } +} diff --git a/angular/test/test-app/src/app/navigation-page2/navigation-page2.component.html b/angular/test/test-app/src/app/navigation-page2/navigation-page2.component.html new file mode 100644 index 0000000000..4c02092489 --- /dev/null +++ b/angular/test/test-app/src/app/navigation-page2/navigation-page2.component.html @@ -0,0 +1,3 @@ +

+ navigation-page2 works! +

diff --git a/angular/test/test-app/src/app/navigation-page2/navigation-page2.component.ts b/angular/test/test-app/src/app/navigation-page2/navigation-page2.component.ts new file mode 100644 index 0000000000..d24c4eb9eb --- /dev/null +++ b/angular/test/test-app/src/app/navigation-page2/navigation-page2.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { NavController } from '@ionic/angular'; + + +@Component({ + selector: 'app-navigation-page2', + templateUrl: './navigation-page2.component.html', +}) +export class NavigationPage2Component { + constructor( + private navController: NavController + ) {} + + ionViewDidEnter() { + this.navController.navigateForward('/navigation/page1'); + } +} diff --git a/angular/test/test-app/src/app/navigation-page3/navigation-page3.component.html b/angular/test/test-app/src/app/navigation-page3/navigation-page3.component.html new file mode 100644 index 0000000000..0294ce8d18 --- /dev/null +++ b/angular/test/test-app/src/app/navigation-page3/navigation-page3.component.html @@ -0,0 +1,3 @@ +

+ navigation-page3 works! +

diff --git a/angular/test/test-app/src/app/navigation-page3/navigation-page3.component.ts b/angular/test/test-app/src/app/navigation-page3/navigation-page3.component.ts new file mode 100644 index 0000000000..a1eddd1300 --- /dev/null +++ b/angular/test/test-app/src/app/navigation-page3/navigation-page3.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import { NavController } from '@ionic/angular'; + +@Component({ + selector: 'app-navigation-page3', + templateUrl: './navigation-page3.component.html', +}) +export class NavigationPage3Component { + constructor( + private navController: NavController + ) {} + +ionViewDidEnter() { + this.navController.navigateRoot('/navigation/page2'); + } +}