From 06c3048828b3fa8aff573acbc4d22249cd2a93f2 Mon Sep 17 00:00:00 2001 From: Asif Rahman Date: Mon, 1 Apr 2019 09:21:14 -0400 Subject: [PATCH] fix(angular): route observables available earlier (#17914) --- .../navigation/ion-router-outlet.ts | 58 +++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/angular/src/directives/navigation/ion-router-outlet.ts b/angular/src/directives/navigation/ion-router-outlet.ts index dbc130208e..a95e45d232 100644 --- a/angular/src/directives/navigation/ion-router-outlet.ts +++ b/angular/src/directives/navigation/ion-router-outlet.ts @@ -175,18 +175,22 @@ export class IonRouterOutlet implements OnDestroy, OnInit { const factory = resolver.resolveComponentFactory(component); const childContexts = this.parentContexts.getOrCreateContext(this.name).children; - const activatedRouteProxy = this.createActivatedRouteProxy(activatedRoute); + + // We create an activated route proxy object that will maintain future updates for this component + // over its lifecycle in the stack. + const component$ = new BehaviorSubject(null); + const activatedRouteProxy = this.createActivatedRouteProxy(component$, activatedRoute); const injector = new OutletInjector(activatedRouteProxy, childContexts, this.location.injector); cmpRef = this.activated = this.location.createComponent(factory, this.location.length, injector); + // Once the component is created we can push it to our local subject supplied to the proxy + component$.next(cmpRef.instance); + // Calling `markForCheck` to make sure we will run the change detection when the // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component. enteringView = this.stackCtrl.createView(this.activated, activatedRoute); - // Once the component is created, use the component instance to setup observables - this.setupProxyObservables(activatedRouteProxy, cmpRef.instance); - // Store references to the proxy by component this.proxyMap.set(cmpRef.instance, activatedRouteProxy); this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute }); @@ -232,43 +236,49 @@ export class IonRouterOutlet implements OnDestroy, OnInit { } /** - * Creates a proxy object that we can use to update activated route properties without losing reference - * in the component injector + * Since the activated route can change over the life time of a component in an ion router outlet, we create + * a proxy so that we can update the values over time as a user navigates back to components already in the stack. */ - private createActivatedRouteProxy(activatedRoute: ActivatedRoute): ActivatedRoute { + private createActivatedRouteProxy(component$: Observable, activatedRoute: ActivatedRoute): ActivatedRoute { const proxy: any = new ActivatedRoute(); + proxy._futureSnapshot = (activatedRoute as any)._futureSnapshot; proxy._routerState = (activatedRoute as any)._routerState; proxy.snapshot = activatedRoute.snapshot; proxy.outlet = activatedRoute.outlet; proxy.component = activatedRoute.component; + // Setup wrappers for the observables so consumers don't have to worry about switching to new observables as the state updates + (proxy as any)._paramMap = this.proxyObservable(component$, 'paramMap'); + (proxy as any)._queryParamMap = this.proxyObservable(component$, 'queryParamMap'); + proxy.url = this.proxyObservable(component$, 'url'); + proxy.params = this.proxyObservable(component$, 'params'); + proxy.queryParams = this.proxyObservable(component$, 'queryParams'); + proxy.fragment = this.proxyObservable(component$, 'fragment'); + proxy.data = this.proxyObservable(component$, 'data'); + return proxy as ActivatedRoute; } - private setupProxyObservables(proxy: ActivatedRoute, component: any): void { - (proxy as any)._paramMap = this.proxyObservable(component, 'paramMap'); - (proxy as any)._queryParamMap = this.proxyObservable(component, 'queryParamMap'); - proxy.url = this.proxyObservable(component, 'url'); - proxy.params = this.proxyObservable(component, 'params'); - proxy.queryParams = this.proxyObservable(component, 'queryParams'); - proxy.fragment = this.proxyObservable(component, 'fragment'); - proxy.data = this.proxyObservable(component, 'data'); - } - /** - * Create a wrapped observable that will switch to the latest activated route matched by the given view id + * Create a wrapped observable that will switch to the latest activated route matched by the given component */ - private proxyObservable(component: any, path: string): Observable { - return this.currentActivatedRoute$.pipe( - filter(current => current !== null && current.component === component), - switchMap(current => current && (current.activatedRoute as any)[path]), - distinctUntilChanged() + private proxyObservable(component$: Observable, path: string): Observable { + return component$.pipe( + // First wait until the component instance is pushed + filter(component => !!component), + switchMap(component => + this.currentActivatedRoute$.pipe( + filter(current => current !== null && current.component === component), + switchMap(current => current && (current.activatedRoute as any)[path]), + distinctUntilChanged() + ) + ) ); } /** - * Updates the given proxy route with data from the new incoming route + * Updates the activated route proxy for the given component to the new incoming router state */ private updateActivatedRouteProxy(component: any, activatedRoute: ActivatedRoute): void { const proxy = this.proxyMap.get(component);