Skip to content

Commit 6527fcb

Browse files
author
vakrilov
committed
fix(router): Handle multiple ActivatedRoutes associated to single palge outlet
1 parent 374bbd4 commit 6527fcb

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

Diff for: nativescript-angular/router/ns-route-reuse-strategy.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from
33

44
import { routeReuseStrategyLog as log } from "../trace";
55
import { NSLocationStrategy } from "./ns-location-strategy";
6-
import { pageRouterActivatedSymbol, destroyComponentRef } from "./page-router-outlet";
6+
import {
7+
destroyComponentRef,
8+
findTopActivatedRouteNodeForOutlet,
9+
pageRouterActivatedSymbol
10+
} from "./page-router-outlet";
711

812
interface CacheItem {
913
key: string;
@@ -47,7 +51,9 @@ class DetachedStateCache {
4751
}
4852

4953
/**
50-
* Does not detach any subtrees. Reuses routes as long as their route config is the same.
54+
* Detaches subtrees loaded inside PageRouterOutlet in forward navigation
55+
* and reattaches them on back.
56+
* Reuses routes as long as their route config is the same.
5157
*/
5258
@Injectable()
5359
export class NSRouteReuseStrategy implements RouteReuseStrategy {
@@ -56,6 +62,8 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy {
5662
constructor(private location: NSLocationStrategy) { }
5763

5864
shouldDetach(route: ActivatedRouteSnapshot): boolean {
65+
route = findTopActivatedRouteNodeForOutlet(route);
66+
5967
const key = getSnapshotKey(route);
6068
const isPageActivated = route[pageRouterActivatedSymbol];
6169
const isBack = this.location._isPageNavigatingBack();
@@ -67,6 +75,8 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy {
6775
}
6876

6977
shouldAttach(route: ActivatedRouteSnapshot): boolean {
78+
route = findTopActivatedRouteNodeForOutlet(route);
79+
7080
const key = getSnapshotKey(route);
7181
const isBack = this.location._isPageNavigatingBack();
7282
const shouldAttach = isBack && this.cache.peek().key === key;
@@ -76,8 +86,9 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy {
7686
return shouldAttach;
7787
}
7888

79-
8089
store(route: ActivatedRouteSnapshot, state: DetachedRouteHandle): void {
90+
route = findTopActivatedRouteNodeForOutlet(route);
91+
8192
const key = getSnapshotKey(route);
8293
log(`store key: ${key}, state: ${state}`);
8394

@@ -95,6 +106,8 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy {
95106
}
96107

97108
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
109+
route = findTopActivatedRouteNodeForOutlet(route);
110+
98111
const key = getSnapshotKey(route);
99112
const isBack = this.location._isPageNavigatingBack();
100113
const cachedItem = this.cache.peek();

Diff for: nativescript-angular/router/page-router-outlet.ts

+50-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from "@angular/core";
88
import {
99
ActivatedRoute,
10+
ActivatedRouteSnapshot,
1011
ChildrenOutletContexts,
1112
PRIMARY_OUTLET,
1213
} from "@angular/router";
@@ -20,7 +21,7 @@ import { BehaviorSubject } from "rxjs/BehaviorSubject";
2021

2122
import { isPresent } from "../lang-facade";
2223
import { DEVICE, PAGE_FACTORY, PageFactory } from "../platform-providers";
23-
import { routerLog } from "../trace";
24+
import { routerLog as log } from "../trace";
2425
import { DetachedLoader } from "../common/detached-loader";
2526
import { ViewUtil } from "../view-util";
2627
import { NSLocationStrategy } from "./ns-location-strategy";
@@ -66,7 +67,40 @@ class ChildInjector implements Injector {
6667

6768
type ProviderMap = Map<Type<any> | InjectionToken<any>, any>;
6869

69-
const log = (msg: string) => routerLog(msg);
70+
/**
71+
* There are cases where multiple activatedRoute nodes should be associated/handled by the same PageRouterOutlet.
72+
* We can gat additional ActivatedRoutes nodes when there is:
73+
* - Lazy loading - there is an additional ActivatedRoute node for the RouteConfig with the `loadChildren` setup
74+
* - Componentless routes - there is an additional ActivatedRoute node for the componentless RouteConfig
75+
*
76+
* Example:
77+
* R <-- root
78+
* |
79+
* feature (lazy module) <-- RouteConfig: { path: "lazy", loadChildren: "./feature/feature.module#FeatureModule" }
80+
* |
81+
* module (componentless route) <-- RouteConfig: { path: "module", children: [...] } // Note: No 'component'
82+
* |
83+
* home <-- RouteConfig: { path: "module", component: MyComponent } - this is what we get as activatedRoute param
84+
*
85+
* In these cases we will mark the top-most node (feature). NSRouteReuseStrategy will detach the tree there and
86+
* use this ActivateRoute as a kay for caching.
87+
*/
88+
export function findTopActivatedRouteNodeForOutlet(activatedRoute: ActivatedRouteSnapshot): ActivatedRouteSnapshot {
89+
let outletActivatedRoute = activatedRoute;
90+
91+
while (outletActivatedRoute.parent &&
92+
outletActivatedRoute.parent.routeConfig &&
93+
!outletActivatedRoute.parent.routeConfig.component) {
94+
95+
outletActivatedRoute = outletActivatedRoute.parent;
96+
}
97+
98+
return outletActivatedRoute;
99+
}
100+
101+
function routeToString(activatedRoute: ActivatedRoute | ActivatedRouteSnapshot): string {
102+
return activatedRoute.pathFromRoot.join("->");
103+
}
70104

71105
@Directive({ selector: "page-router-outlet" }) // tslint:disable-line:directive-selector
72106
export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-line:directive-class-suffix
@@ -181,6 +215,8 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l
181215
throw new Error("Outlet is not activated");
182216
}
183217

218+
log("PageRouterOutlet.detach() - " + routeToString(this._activatedRoute));
219+
184220
const component = this.activated;
185221
this.activated = null;
186222
this._activatedRoute = null;
@@ -191,13 +227,12 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l
191227
* Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
192228
*/
193229
attach(ref: ComponentRef<any>, activatedRoute: ActivatedRoute) {
194-
log("PageRouterOutlet.attach()" +
195-
"when RouteReuseStrategy instructs to re-attach " +
196-
"previously detached subtree");
230+
log("PageRouterOutlet.attach() - " + routeToString(activatedRoute));
197231

198232
this.activated = ref;
199233
this._activatedRoute = activatedRoute;
200-
this._activatedRoute.snapshot[pageRouterActivatedSymbol] = true;
234+
235+
this.markActivatedRoute(activatedRoute);
201236

202237
this.locationStrategy._finishBackPageNavigation();
203238
}
@@ -215,10 +250,11 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l
215250
throw new Error("Currently in page back navigation - component should be reattached instead of activated.");
216251
}
217252

218-
log("PageRouterOutlet.activateWith() - instantiating new component");
253+
log("PageRouterOutlet.activateWith() - " + routeToString(activatedRoute));
219254

220255
this._activatedRoute = activatedRoute;
221-
this._activatedRoute.snapshot[pageRouterActivatedSymbol] = true;
256+
257+
this.markActivatedRoute(activatedRoute);
222258

223259
resolver = resolver || this.resolver;
224260

@@ -318,6 +354,12 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l
318354
});
319355
}
320356

357+
private markActivatedRoute(activatedRoute: ActivatedRoute) {
358+
const nodeToMark = findTopActivatedRouteNodeForOutlet(activatedRoute.snapshot);
359+
nodeToMark[pageRouterActivatedSymbol] = true;
360+
log("Activated route marked as page: " + routeToString(nodeToMark));
361+
}
362+
321363
// NOTE: Using private APIs - potential break point!
322364
private getComponentFactory(
323365
activatedRoute: any,

0 commit comments

Comments
 (0)