Skip to content

Commit d9a7c63

Browse files
daem0ndevmhartington
authored andcommitted
fix(angular): support relative router links
Closes#17888, closes#16534, closes#16736, closes#16954
1 parent f2c8db9 commit d9a7c63

1 file changed

Lines changed: 75 additions & 5 deletions

File tree

‎angular/src/directives/navigation/ion-router-outlet.ts‎

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import{Attribute,ChangeDetectorRef,ComponentFactoryResolver,ComponentRef,Directive,ElementRef,EventEmitter,Injector,NgZone,OnDestroy,OnInit,Optional,Output,SkipSelf,ViewContainerRef}from'@angular/core';
22
import{ActivatedRoute,ChildrenOutletContexts,OutletContext,PRIMARY_OUTLET,Router}from'@angular/router';
3-
3+
import{BehaviorSubject,Observable}from'rxjs';
4+
import{distinctUntilChanged,filter,switchMap}from'rxjs/operators';
45
import{Config}from'../../providers/config';
56
import{NavController}from'../../providers/nav-controller';
6-
77
import{StackController}from'./stack-controller';
8-
import{RouteView,getUrl}from'./stack-utils';
8+
import{getUrl,RouteView}from'./stack-utils';
99

1010
@Directive({
1111
selector: 'ion-router-outlet',
1212
exportAs: 'outlet',
1313
inputs: ['animated','swipeGesture']
1414
})
1515
exportclassIonRouterOutletimplementsOnDestroy,OnInit{
16-
1716
privateactivated: ComponentRef<any>|null=null;
1817
privateactivatedView: RouteView|null=null;
1918

@@ -23,6 +22,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
2322
privatestackCtrl: StackController;
2423
privatenativeEl: HTMLIonRouterOutletElement;
2524

25+
// Maintain map of activated route proxies for each component instance
26+
privateproxyMap=newWeakMap<any,ActivatedRoute>();
27+
28+
// Keep the latest activated route in a subject for the proxy routes to switch map to
29+
privatecurrentActivatedRoute$=newBehaviorSubject<{component: any;activatedRoute: ActivatedRoute}|null>(null);
30+
2631
tabsPrefix: string|undefined;
2732

2833
@Output()stackEvents=newEventEmitter<any>();
@@ -159,20 +164,31 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
159164
constcontext=this.getContext()!;
160165
context.children['contexts']=saved;
161166
}
167+
// Updated activated route proxy for this component
168+
this.updateActivatedRouteProxy(cmpRef.instance,activatedRoute);
162169
}else{
163170
constsnapshot=(activatedRouteasany)._futureSnapshot;
164171
constcomponent=snapshot.routeConfig!.componentasany;
165172
resolver=resolver||this.resolver;
166173

167174
constfactory=resolver.resolveComponentFactory(component);
168175
constchildContexts=this.parentContexts.getOrCreateContext(this.name).children;
176+
constactivatedRouteProxy=this.createActivatedRouteProxy(activatedRoute);
169177

170-
constinjector=newOutletInjector(activatedRoute,childContexts,this.location.injector);
178+
constinjector=newOutletInjector(activatedRouteProxy,childContexts,this.location.injector);
171179
cmpRef=this.activated=this.location.createComponent(factory,this.location.length,injector);
172180

173181
// Calling `markForCheck` to make sure we will run the change detection when the
174182
// `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
175183
enteringView=this.stackCtrl.createView(this.activated,activatedRoute);
184+
185+
// Once the component is created, use the component instance to setup observables
186+
this.setupProxyObservables(activatedRouteProxy,cmpRef.instance);
187+
188+
// Store references to the proxy by component
189+
this.proxyMap.set(cmpRef.instance,activatedRouteProxy);
190+
this.currentActivatedRoute$.next({component: cmpRef.instance, activatedRoute });
191+
176192
this.changeDetector.markForCheck();
177193
}
178194

@@ -212,6 +228,60 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
212228
getActiveStackId(): string|undefined{
213229
returnthis.stackCtrl.getActiveStackId();
214230
}
231+
232+
/**
233+
* Creates a proxy object that we can use to update activated route properties without losing reference
234+
* in the component injector
235+
*/
236+
privatecreateActivatedRouteProxy(activatedRoute: ActivatedRoute): ActivatedRoute{
237+
constproxy: any=newActivatedRoute();
238+
proxy._futureSnapshot=(activatedRouteasany)._futureSnapshot;
239+
proxy._routerState=(activatedRouteasany)._routerState;
240+
proxy.snapshot=activatedRoute.snapshot;
241+
proxy.outlet=activatedRoute.outlet;
242+
proxy.component=activatedRoute.component;
243+
244+
returnproxyasActivatedRoute;
245+
}
246+
247+
privatesetupProxyObservables(proxy: ActivatedRoute,component: any): void{
248+
(proxyasany)._paramMap=this.proxyObservable(component,'paramMap');
249+
(proxyasany)._queryParamMap=this.proxyObservable(component,'queryParamMap');
250+
proxy.url=this.proxyObservable(component,'url');
251+
proxy.params=this.proxyObservable(component,'params');
252+
proxy.queryParams=this.proxyObservable(component,'queryParams');
253+
proxy.fragment=this.proxyObservable(component,'fragment');
254+
proxy.data=this.proxyObservable(component,'data');
255+
}
256+
257+
/**
258+
* Create a wrapped observable that will switch to the latest activated route matched by the given view id
259+
*/
260+
privateproxyObservable(component: any,path: string): Observable<any>{
261+
returnthis.currentActivatedRoute$.pipe(
262+
filter(current=>current!==null&&current.component===component),
263+
switchMap(current=>current&&(current.activatedRouteasany)[path]),
264+
distinctUntilChanged()
265+
);
266+
}
267+
268+
/**
269+
* Updates the given proxy route with data from the new incoming route
270+
*/
271+
privateupdateActivatedRouteProxy(component: any,activatedRoute: ActivatedRoute): void{
272+
constproxy=this.proxyMap.get(component);
273+
if(!proxy){
274+
thrownewError(`Could not find activated route proxy for view`);
275+
}
276+
277+
(proxyasany)._futureSnapshot=(activatedRouteasany)._futureSnapshot;
278+
(proxyasany)._routerState=(activatedRouteasany)._routerState;
279+
proxy.snapshot=activatedRoute.snapshot;
280+
proxy.outlet=activatedRoute.outlet;
281+
proxy.component=activatedRoute.component;
282+
283+
this.currentActivatedRoute$.next({ component, activatedRoute });
284+
}
215285
}
216286

217287
classOutletInjectorimplementsInjector{

0 commit comments

Comments
 (0)