@@ -30,6 +30,7 @@ import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader';
30
30
import { RouterOutletMap } from './router_outlet_map' ;
31
31
import { ActivatedRoute , ActivatedRouteSnapshot , RouterState , RouterStateSnapshot , advanceActivatedRoute , createEmptyState } from './router_state' ;
32
32
import { NavigationCancelingError , PRIMARY_OUTLET , Params } from './shared' ;
33
+ import { DefaultUrlHandlingStrategy , UrlHandlingStrategy } from './url_handling_strategy' ;
33
34
import { UrlSerializer , UrlTree , containsTree , createEmptyUrlTree } from './url_tree' ;
34
35
import { andObservables , forEach , merge , shallowEqual , waitForMap , wrapIntoObservable } from './utils/collection' ;
35
36
import { TreeNode } from './utils/tree' ;
@@ -285,6 +286,9 @@ function defaultErrorHandler(error: any): any {
285
286
*/
286
287
export class Router {
287
288
private currentUrlTree : UrlTree ;
289
+ private rawUrlTree : UrlTree ;
290
+ private lastNavigation : UrlTree ;
291
+
288
292
private currentRouterState : RouterState ;
289
293
private locationSubscription : Subscription ;
290
294
private routerEvents : Subject < Event > ;
@@ -303,6 +307,11 @@ export class Router {
303
307
*/
304
308
navigated : boolean = false ;
305
309
310
+ /**
311
+ * Extracts and merges URLs. Used for Angular 1 to Angular 2 migrations.
312
+ */
313
+ urlHandlingStrategy : UrlHandlingStrategy = new DefaultUrlHandlingStrategy ( ) ;
314
+
306
315
/**
307
316
* Creates the router service.
308
317
*/
@@ -314,6 +323,7 @@ export class Router {
314
323
this . resetConfig ( config ) ;
315
324
this . routerEvents = new Subject < Event > ( ) ;
316
325
this . currentUrlTree = createEmptyUrlTree ( ) ;
326
+ this . rawUrlTree = this . currentUrlTree ;
317
327
this . configLoader = new RouterConfigLoader ( loader , compiler ) ;
318
328
this . currentRouterState = createEmptyState ( this . currentUrlTree , this . rootComponentType ) ;
319
329
}
@@ -344,12 +354,20 @@ export class Router {
344
354
// Zone.current.wrap is needed because of the issue with RxJS scheduler,
345
355
// which does not work properly with zone.js in IE and Safari
346
356
this . locationSubscription = < any > this . location . subscribe ( Zone . current . wrap ( ( change : any ) => {
347
- const tree = this . urlSerializer . parse ( change [ 'url' ] ) ;
348
- // we fire multiple events for a single URL change
349
- // we should navigate only once
350
- return this . currentUrlTree . toString ( ) !== tree . toString ( ) ?
351
- this . scheduleNavigation ( tree , { skipLocationChange : change [ 'pop' ] , replaceUrl : true } ) :
352
- null ;
357
+ const rawUrlTree = this . urlSerializer . parse ( change [ 'url' ] ) ;
358
+ const tree = this . urlHandlingStrategy . extract ( rawUrlTree ) ;
359
+
360
+
361
+ setTimeout ( ( ) => {
362
+ // we fire multiple events for a single URL change
363
+ // we should navigate only once
364
+ if ( ! this . lastNavigation || this . lastNavigation . toString ( ) !== tree . toString ( ) ) {
365
+ this . scheduleNavigation (
366
+ rawUrlTree , tree , { skipLocationChange : change [ 'pop' ] , replaceUrl : true } ) ;
367
+ } else {
368
+ this . rawUrlTree = rawUrlTree ;
369
+ }
370
+ } , 0 ) ;
353
371
} ) ) ;
354
372
}
355
373
@@ -470,10 +488,10 @@ export class Router {
470
488
navigateByUrl ( url : string | UrlTree , extras : NavigationExtras = { skipLocationChange : false } ) :
471
489
Promise < boolean > {
472
490
if ( url instanceof UrlTree ) {
473
- return this . scheduleNavigation ( url , extras ) ;
491
+ return this . scheduleNavigation ( this . rawUrlTree , url , extras ) ;
474
492
} else {
475
493
const urlTree = this . urlSerializer . parse ( url ) ;
476
- return this . scheduleNavigation ( urlTree , extras ) ;
494
+ return this . scheduleNavigation ( this . rawUrlTree , urlTree , extras ) ;
477
495
}
478
496
}
479
497
@@ -500,7 +518,7 @@ export class Router {
500
518
*/
501
519
navigate ( commands : any [ ] , extras : NavigationExtras = { skipLocationChange : false } ) :
502
520
Promise < boolean > {
503
- return this . scheduleNavigation ( this . createUrlTree ( commands , extras ) , extras ) ;
521
+ return this . scheduleNavigation ( this . rawUrlTree , this . createUrlTree ( commands , extras ) , extras ) ;
504
522
}
505
523
506
524
/**
@@ -525,16 +543,34 @@ export class Router {
525
543
}
526
544
}
527
545
528
- private scheduleNavigation ( url : UrlTree , extras : NavigationExtras ) : Promise < boolean > {
529
- const id = ++ this . navigationId ;
530
- this . routerEvents . next ( new NavigationStart ( id , this . serializeUrl ( url ) ) ) ;
531
- return Promise . resolve ( ) . then (
532
- ( _ ) => this . runNavigate ( url , extras . skipLocationChange , extras . replaceUrl , id ) ) ;
546
+ private scheduleNavigation ( rawUrl : UrlTree , url : UrlTree , extras : NavigationExtras ) :
547
+ Promise < boolean > {
548
+ if ( this . urlHandlingStrategy . shouldProcessUrl ( url ) ) {
549
+ const id = ++ this . navigationId ;
550
+ this . routerEvents . next ( new NavigationStart ( id , this . serializeUrl ( url ) ) ) ;
551
+
552
+ return Promise . resolve ( ) . then (
553
+ ( _ ) => this . runNavigate (
554
+ rawUrl , url , extras . skipLocationChange , extras . replaceUrl , id , null ) ) ;
555
+
556
+ // we cannot process the current URL, but we could process the previous one =>
557
+ // we need to do some cleanup
558
+ } else if ( this . urlHandlingStrategy . shouldProcessUrl ( this . rawUrlTree ) ) {
559
+ const id = ++ this . navigationId ;
560
+ this . routerEvents . next ( new NavigationStart ( id , this . serializeUrl ( url ) ) ) ;
561
+
562
+ return Promise . resolve ( ) . then (
563
+ ( _ ) => this . runNavigate (
564
+ rawUrl , url , false , false , id , createEmptyState ( url , this . rootComponentType ) ) ) ;
565
+ } else {
566
+ this . rawUrlTree = rawUrl ;
567
+ return Promise . resolve ( null ) ;
568
+ }
533
569
}
534
570
535
571
private runNavigate (
536
- url : UrlTree , shouldPreventPushState : boolean , shouldReplaceUrl : boolean ,
537
- id : number ) : Promise < boolean > {
572
+ rawUrl : UrlTree , url : UrlTree , shouldPreventPushState : boolean , shouldReplaceUrl : boolean ,
573
+ id : number , precreatedState : RouterState ) : Promise < boolean > {
538
574
if ( id !== this . navigationId ) {
539
575
this . location . go ( this . urlSerializer . serialize ( this . currentUrlTree ) ) ;
540
576
this . routerEvents . next ( new NavigationCancel (
@@ -553,23 +589,33 @@ export class Router {
553
589
const storedState = this . currentRouterState ;
554
590
const storedUrl = this . currentUrlTree ;
555
591
556
- const redirectsApplied$ = applyRedirects ( this . injector , this . configLoader , url , this . config ) ;
557
-
558
- const snapshot$ = mergeMap . call ( redirectsApplied$ , ( u : UrlTree ) => {
559
- appliedUrl = u ;
560
- return recognize (
561
- this . rootComponentType , this . config , appliedUrl , this . serializeUrl ( appliedUrl ) ) ;
562
- } ) ;
563
-
564
- const emitRecognzied$ = map . call ( snapshot$ , ( newRouterStateSnapshot : RouterStateSnapshot ) => {
565
- this . routerEvents . next ( new RoutesRecognized (
566
- id , this . serializeUrl ( url ) , this . serializeUrl ( appliedUrl ) , newRouterStateSnapshot ) ) ;
567
- return newRouterStateSnapshot ;
568
- } ) ;
569
-
570
- const routerState$ = map . call ( emitRecognzied$ , ( routerStateSnapshot : RouterStateSnapshot ) => {
571
- return createRouterState ( routerStateSnapshot , this . currentRouterState ) ;
572
- } ) ;
592
+ let routerState$ : any ;
593
+
594
+ if ( ! precreatedState ) {
595
+ const redirectsApplied$ =
596
+ applyRedirects ( this . injector , this . configLoader , url , this . config ) ;
597
+
598
+ const snapshot$ = mergeMap . call ( redirectsApplied$ , ( u : UrlTree ) => {
599
+ appliedUrl = u ;
600
+ return recognize (
601
+ this . rootComponentType , this . config , appliedUrl , this . serializeUrl ( appliedUrl ) ) ;
602
+ } ) ;
603
+
604
+ const emitRecognzied$ =
605
+ map . call ( snapshot$ , ( newRouterStateSnapshot : RouterStateSnapshot ) => {
606
+ this . routerEvents . next ( new RoutesRecognized (
607
+ id , this . serializeUrl ( url ) , this . serializeUrl ( appliedUrl ) ,
608
+ newRouterStateSnapshot ) ) ;
609
+ return newRouterStateSnapshot ;
610
+ } ) ;
611
+
612
+ routerState$ = map . call ( emitRecognzied$ , ( routerStateSnapshot : RouterStateSnapshot ) => {
613
+ return createRouterState ( routerStateSnapshot , this . currentRouterState ) ;
614
+ } ) ;
615
+ } else {
616
+ appliedUrl = url ;
617
+ routerState$ = of ( precreatedState ) ;
618
+ }
573
619
574
620
const preactivation$ = map . call ( routerState$ , ( newState : RouterState ) => {
575
621
state = newState ;
@@ -595,11 +641,14 @@ export class Router {
595
641
return ;
596
642
}
597
643
644
+ this . lastNavigation = appliedUrl ;
598
645
this . currentUrlTree = appliedUrl ;
646
+ this . rawUrlTree = this . urlHandlingStrategy . merge ( this . currentUrlTree , rawUrl ) ;
647
+
599
648
this . currentRouterState = state ;
600
649
601
650
if ( ! shouldPreventPushState ) {
602
- let path = this . urlSerializer . serialize ( appliedUrl ) ;
651
+ let path = this . urlSerializer . serialize ( this . rawUrlTree ) ;
603
652
if ( this . location . isCurrentPathEqualTo ( path ) || shouldReplaceUrl ) {
604
653
this . location . replaceState ( path ) ;
605
654
} else {
@@ -641,7 +690,8 @@ export class Router {
641
690
if ( id === this . navigationId ) {
642
691
this . currentRouterState = storedState ;
643
692
this . currentUrlTree = storedUrl ;
644
- this . location . replaceState ( this . serializeUrl ( storedUrl ) ) ;
693
+ this . rawUrlTree = this . urlHandlingStrategy . merge ( this . currentUrlTree , rawUrl ) ;
694
+ this . location . replaceState ( this . serializeUrl ( this . rawUrlTree ) ) ;
645
695
}
646
696
} ) ;
647
697
} ) ;
0 commit comments