From 943e2f73c0786c83adc736ae4cd48993a8803d8b Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Wed, 21 Mar 2018 19:34:22 +0100 Subject: [PATCH] fix(angular): router-outlet animation --- angular/src/navigation/ion-router-outlet.ts | 91 ++++++++++--------- demos/angular/src/app/app-routing.module.ts | 2 +- .../app/home-page/home-page.component.html | 36 +++----- .../page-one/page-one-routing.module.ts | 14 +++ .../simple-nav/page-one/page-one.module.ts | 17 ++++ .../src/app/simple-nav/page-one/page-one.ts | 28 ++++++ .../page-three/page-three-routing.module.ts | 14 +++ .../page-three/page-three.module.ts | 19 ++++ .../app/simple-nav/page-three/page-three.ts | 40 ++++++++ .../page-two/page-two-routing.module.ts | 17 ++++ .../simple-nav/page-two/page-two.module.ts | 25 +++++ .../src/app/simple-nav/page-two/page-two.ts | 26 ++++++ .../simple-nav/simple-nav-routing.module.ts | 22 +++++ .../app/simple-nav/simple-nav.component.ts | 11 +++ .../src/app/simple-nav/simple-nav.module.ts | 20 ++++ 15 files changed, 315 insertions(+), 67 deletions(-) create mode 100755 demos/angular/src/app/simple-nav/page-one/page-one-routing.module.ts create mode 100755 demos/angular/src/app/simple-nav/page-one/page-one.module.ts create mode 100755 demos/angular/src/app/simple-nav/page-one/page-one.ts create mode 100755 demos/angular/src/app/simple-nav/page-three/page-three-routing.module.ts create mode 100755 demos/angular/src/app/simple-nav/page-three/page-three.module.ts create mode 100755 demos/angular/src/app/simple-nav/page-three/page-three.ts create mode 100755 demos/angular/src/app/simple-nav/page-two/page-two-routing.module.ts create mode 100755 demos/angular/src/app/simple-nav/page-two/page-two.module.ts create mode 100755 demos/angular/src/app/simple-nav/page-two/page-two.ts create mode 100755 demos/angular/src/app/simple-nav/simple-nav-routing.module.ts create mode 100755 demos/angular/src/app/simple-nav/simple-nav.component.ts create mode 100755 demos/angular/src/app/simple-nav/simple-nav.module.ts diff --git a/angular/src/navigation/ion-router-outlet.ts b/angular/src/navigation/ion-router-outlet.ts index 64e0559e7c..6e5955d91d 100644 --- a/angular/src/navigation/ion-router-outlet.ts +++ b/angular/src/navigation/ion-router-outlet.ts @@ -1,30 +1,32 @@ -import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, OnDestroy, OnInit, Output, ViewContainerRef } from '@angular/core'; -import { ActivatedRoute, ChildrenOutletContexts } from '@angular/router'; -import * as ctrl from './router-controller'; -import { runTransition } from './router-transition'; +import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, OnDestroy, OnInit, Output, ViewContainerRef, ElementRef } from '@angular/core'; +import { ActivatedRoute, ChildrenOutletContexts, PRIMARY_OUTLET } from '@angular/router'; - -@Directive({selector: 'ion-router-outlet', exportAs: 'ionOutlet'}) +@Directive({ + selector: 'ion-router-outlet', + exportAs: 'outlet' +}) export class IonRouterOutlet implements OnDestroy, OnInit { + private activated: ComponentRef|null = null; + private deactivated: ComponentRef|null = null; + private _activatedRoute: ActivatedRoute|null = null; private name: string; - private views: ctrl.RouteView[] = []; - @Output('activate') activateEvents = new EventEmitter(); @Output('deactivate') deactivateEvents = new EventEmitter(); constructor( private parentContexts: ChildrenOutletContexts, private location: ViewContainerRef, - private resolver: ComponentFactoryResolver, @Attribute('name') name: string, + private resolver: ComponentFactoryResolver, + private elementRef: ElementRef, + @Attribute('name') name: string, private changeDetector: ChangeDetectorRef) { - this.name = name || 'primary'; + this.name = name || PRIMARY_OUTLET; parentContexts.onChildOutletCreated(this.name, this as any); } ngOnDestroy(): void { - ctrl.destoryViews(this.views); this.parentContexts.onChildOutletDestroyed(this.name); } @@ -82,69 +84,68 @@ export class IonRouterOutlet implements OnDestroy, OnInit { attach(ref: ComponentRef, activatedRoute: ActivatedRoute) { this.activated = ref; this._activatedRoute = activatedRoute; - - ctrl.attachView(this.views, this.location, ref, activatedRoute); + this.location.insert(ref.hostView); } deactivate(): void { if (this.activated) { const c = this.component; - - ctrl.deactivateView(this.views, this.activated); - + this.deactivated = this.activated; this.activated = null; this._activatedRoute = null; this.deactivateEvents.emit(c); } } - activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null) { + async activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null) { if (this.isActivated) { throw new Error('Cannot activate an already activated outlet'); } - this._activatedRoute = activatedRoute; + const snapshot = (activatedRoute as any)._futureSnapshot; - const existingView = ctrl.getExistingView(this.views, activatedRoute); - if (existingView) { - // we've already got a view hanging around - this.activated = existingView.ref; + const component = snapshot.routeConfig !.component; + resolver = resolver || this.resolver; - } else { - // haven't created this view yet - const snapshot = (activatedRoute as any)._futureSnapshot; + const factory = resolver.resolveComponentFactory(component); + const childContexts = this.parentContexts.getOrCreateContext(this.name).children; - const component = snapshot.routeConfig !.component; - resolver = resolver || this.resolver; - - const factory = resolver.resolveComponentFactory(component); - const childContexts = this.parentContexts.getOrCreateContext(this.name).children; - - const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector); - this.activated = this.location.createComponent(factory, this.location.length, injector); - - // keep a ref - ctrl.initRouteViewElm(this.views, this.activated, activatedRoute); - } + const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector); + this.activated = this.location.createComponent(factory, this.location.length, injector); // Calling `markForCheck` to make sure we will run the change detection when the // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component. this.changeDetector.markForCheck(); + await this.transition(); + this.activateEvents.emit(this.activated.instance); + } - const lastDeactivatedRef = ctrl.getLastDeactivatedRef(this.views); + async transition() { + const enteringRef = this.activated; + const enteringEl = (enteringRef && enteringRef.location && enteringRef.location.nativeElement) as HTMLElement; + if (enteringEl) { + enteringEl.classList.add('ion-page', 'hide-page'); - runTransition(this.activated, lastDeactivatedRef).then(() => { - console.log('transition end'); - this.activateEvents.emit(this.activated.instance); - }); + const navEl = this.elementRef.nativeElement; + navEl.appendChild(enteringEl); + + await navEl.componentOnReady(); + await navEl.commit(enteringEl); + + if (this.deactivated) { + this.deactivated.destroy(); + this.deactivated = null; + } + } } } - class OutletInjector implements Injector { constructor( - private route: ActivatedRoute, private childContexts: ChildrenOutletContexts, - private parent: Injector) {} + private route: ActivatedRoute, + private childContexts: ChildrenOutletContexts, + private parent: Injector + ) {} get(token: any, notFoundValue?: any): any { if (token === ActivatedRoute) { diff --git a/demos/angular/src/app/app-routing.module.ts b/demos/angular/src/app/app-routing.module.ts index 72460e7956..248bed696e 100644 --- a/demos/angular/src/app/app-routing.module.ts +++ b/demos/angular/src/app/app-routing.module.ts @@ -21,7 +21,7 @@ const routes: Routes = [ { path: 'virtual-scroll', loadChildren: 'app/virtual-scroll/virtual-scroll.module#VirtualScrollModule' }, { path: 'no-routing-nav', loadChildren: 'app/no-routing-nav/no-routing-nav.module#NoRoutingNavModule' }, - + { path: 'simple-nav', loadChildren: 'app/simple-nav/simple-nav.module#SimpleNavModule' }, ]; @NgModule({ diff --git a/demos/angular/src/app/home-page/home-page.component.html b/demos/angular/src/app/home-page/home-page.component.html index 108a89feb4..76e03dd8dd 100644 --- a/demos/angular/src/app/home-page/home-page.component.html +++ b/demos/angular/src/app/home-page/home-page.component.html @@ -5,49 +5,49 @@ @@ -61,12 +61,6 @@
  • Simple Nav
  • -
  • - Nested Nav -
  • -
  • - Nav Then Tabs -
  • diff --git a/demos/angular/src/app/simple-nav/page-one/page-one-routing.module.ts b/demos/angular/src/app/simple-nav/page-one/page-one-routing.module.ts new file mode 100755 index 0000000000..25ae75ef7d --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-one/page-one-routing.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { PageOne } from './page-one'; + +const routes: Routes = [ + { path: '', component: PageOne} +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class PageOneRoutingModule { } diff --git a/demos/angular/src/app/simple-nav/page-one/page-one.module.ts b/demos/angular/src/app/simple-nav/page-one/page-one.module.ts new file mode 100755 index 0000000000..91b73d4c54 --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-one/page-one.module.ts @@ -0,0 +1,17 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { PageOne } from './page-one'; +import { PageOneRoutingModule } from './page-one-routing.module'; + +@NgModule({ + imports: [ + CommonModule, + PageOneRoutingModule + ], + declarations: [PageOne], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] +}) +export class PageOneModule { } diff --git a/demos/angular/src/app/simple-nav/page-one/page-one.ts b/demos/angular/src/app/simple-nav/page-one/page-one.ts new file mode 100755 index 0000000000..2ba46f74e7 --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-one/page-one.ts @@ -0,0 +1,28 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { PageTwo } from '../page-two/page-two'; + +@Component({ + selector: 'page-one', + template: ` + + + Simple Page One + + + + Page One - {{ts}} +
    + Go to Page Two +
    +
    + ` +}) +export class PageOne { + + ts: number; + constructor() { + setInterval(() => { + this.ts = Date.now(); + }, 500); + } +} diff --git a/demos/angular/src/app/simple-nav/page-three/page-three-routing.module.ts b/demos/angular/src/app/simple-nav/page-three/page-three-routing.module.ts new file mode 100755 index 0000000000..d534208893 --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-three/page-three-routing.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { PageThree } from './page-three'; + +const routes: Routes = [ + { path: '', component: PageThree } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class PageThreeRoutingModule { } diff --git a/demos/angular/src/app/simple-nav/page-three/page-three.module.ts b/demos/angular/src/app/simple-nav/page-three/page-three.module.ts new file mode 100755 index 0000000000..1f75e6350f --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-three/page-three.module.ts @@ -0,0 +1,19 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { PageThree } from './page-three'; +import { PageThreeRoutingModule } from './page-three-routing.module'; + +@NgModule({ + imports: [ + CommonModule, + PageThreeRoutingModule + ], + declarations: [ + PageThree + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] +}) +export class PageThreeModule { } diff --git a/demos/angular/src/app/simple-nav/page-three/page-three.ts b/demos/angular/src/app/simple-nav/page-three/page-three.ts new file mode 100755 index 0000000000..1b6cc4e64a --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-three/page-three.ts @@ -0,0 +1,40 @@ +import { Component, ViewEncapsulation } from '@angular/core'; + +@Component({ + selector: 'page-three', + template: ` + + + Page Three + + + + Page Three {{ts}} +
    isProd: {{isProd}}
    +
    paramOne: {{paramOne}}
    +
    paramTwo: {{paramTwo}}
    +
    + Go Back +
    +
    + ` +}) +export class PageThree { + + ts: number; + isProd = false; + paramOne: any = null; + paramTwo: any = null; + + // constructor(private navController: NavController, private navParams: NavParams) { + + // this.isProd = navParams.get('isProd'); + // this.paramOne = navParams.get('paramOne'); + // this.paramTwo = navParams.get('paramTwo'); + + // setInterval(() => { + // this.ts = Date.now(); + // }, 500); + // } + +} diff --git a/demos/angular/src/app/simple-nav/page-two/page-two-routing.module.ts b/demos/angular/src/app/simple-nav/page-two/page-two-routing.module.ts new file mode 100755 index 0000000000..4700b6c8b5 --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-two/page-two-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { PageTwo } from './page-two'; + +const routes: Routes = [ + { + path: '', + component: PageTwo, + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class PageTwoRoutingModule { } diff --git a/demos/angular/src/app/simple-nav/page-two/page-two.module.ts b/demos/angular/src/app/simple-nav/page-two/page-two.module.ts new file mode 100755 index 0000000000..65450c1e2a --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-two/page-two.module.ts @@ -0,0 +1,25 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { + IonicAngularModule, +} from '@ionic/angular'; + +import { PageTwo } from './page-two'; +import { PageTwoRoutingModule } from './page-two-routing.module'; + + +@NgModule({ + imports: [ + CommonModule, + PageTwoRoutingModule, + IonicAngularModule, + ], + declarations: [ + PageTwo, + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] +}) +export class PageTwoModule { } diff --git a/demos/angular/src/app/simple-nav/page-two/page-two.ts b/demos/angular/src/app/simple-nav/page-two/page-two.ts new file mode 100755 index 0000000000..86a9ce4269 --- /dev/null +++ b/demos/angular/src/app/simple-nav/page-two/page-two.ts @@ -0,0 +1,26 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { Location } from '@angular/common'; + + +@Component({ + selector: 'page-two', + template: ` + + + Page Two + + + + Page Two - {{ts}} +
    + Go to Page Three +
    +
    + Go Back +
    +
    + ` +}) +export class PageTwo { + +} diff --git a/demos/angular/src/app/simple-nav/simple-nav-routing.module.ts b/demos/angular/src/app/simple-nav/simple-nav-routing.module.ts new file mode 100755 index 0000000000..5a4ad2c1b3 --- /dev/null +++ b/demos/angular/src/app/simple-nav/simple-nav-routing.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { SimpleNavPageComponent } from './simple-nav.component'; + +const routes: Routes = [ + { + path: '', + component: SimpleNavPageComponent, + children: [ + { path: 'page-one', loadChildren: './page-one/page-one.module#PageOneModule' }, + { path: 'page-two', loadChildren: './page-two/page-two.module#PageTwoModule' }, + { path: 'page-three/:paramOne/:paramTwo', loadChildren: './page-three/page-three.module#PageThreeModule', data: { isProd: true} } + ] + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class SimpleNavRoutingModule { } diff --git a/demos/angular/src/app/simple-nav/simple-nav.component.ts b/demos/angular/src/app/simple-nav/simple-nav.component.ts new file mode 100755 index 0000000000..7cab68c2f0 --- /dev/null +++ b/demos/angular/src/app/simple-nav/simple-nav.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-nav-page', + template: ` + + + + ` +}) +export class SimpleNavPageComponent {} diff --git a/demos/angular/src/app/simple-nav/simple-nav.module.ts b/demos/angular/src/app/simple-nav/simple-nav.module.ts new file mode 100755 index 0000000000..2da989f7c7 --- /dev/null +++ b/demos/angular/src/app/simple-nav/simple-nav.module.ts @@ -0,0 +1,20 @@ +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; + +import { SimpleNavPageComponent } from './simple-nav.component'; +import { SimpleNavRoutingModule } from './simple-nav-routing.module'; + +import { IonicAngularModule } from '@ionic/angular'; + +@NgModule({ + declarations: [ + SimpleNavPageComponent, + ], + imports: [ + CommonModule, + IonicAngularModule, + SimpleNavRoutingModule + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class SimpleNavModule {}