feat(router): make activation sync
This commit is contained in:
		
							parent
							
								
									6f052d1daf
								
							
						
					
					
						commit
						9ff6b0828f
					
				| @ -3,10 +3,12 @@ import { Location } from '@angular/common'; | |||||||
| import { UrlSerializer } from './url_serializer'; | import { UrlSerializer } from './url_serializer'; | ||||||
| import { RouterOutletMap } from './router_outlet_map'; | import { RouterOutletMap } from './router_outlet_map'; | ||||||
| import { recognize } from './recognize'; | import { recognize } from './recognize'; | ||||||
| import { rootNode, TreeNode } from './utils/tree'; | import { resolve } from './resolve'; | ||||||
|  | import { createRouterState } from './create_router_state'; | ||||||
|  | import { TreeNode } from './utils/tree'; | ||||||
| import { UrlTree, createEmptyUrlTree } from './url_tree'; | import { UrlTree, createEmptyUrlTree } from './url_tree'; | ||||||
| import { PRIMARY_OUTLET, Params } from './shared'; | import { PRIMARY_OUTLET, Params } from './shared'; | ||||||
| import { createEmptyState, RouterState, ActivatedRoute} from './router_state'; | import { createEmptyState, createEmptyStateCandidate, RouterState, RouterStateCandidate, ActivatedRoute, ActivatedRouteCandidate} from './router_state'; | ||||||
| import { RouterConfig } from './config'; | import { RouterConfig } from './config'; | ||||||
| import { RouterOutlet } from './directives/router_outlet'; | import { RouterOutlet } from './directives/router_outlet'; | ||||||
| import { createUrlTree } from './create_url_tree'; | import { createUrlTree } from './create_url_tree'; | ||||||
| @ -15,9 +17,6 @@ import { Observable } from 'rxjs/Observable'; | |||||||
| import { Subscription } from 'rxjs/Subscription'; | import { Subscription } from 'rxjs/Subscription'; | ||||||
| import 'rxjs/add/operator/map'; | import 'rxjs/add/operator/map'; | ||||||
| import 'rxjs/add/operator/mergeMap'; | import 'rxjs/add/operator/mergeMap'; | ||||||
| import 'rxjs/add/operator/toPromise'; |  | ||||||
| import {fromPromise} from 'rxjs/observable/fromPromise'; |  | ||||||
| import {forkJoin} from 'rxjs/observable/forkJoin'; |  | ||||||
| 
 | 
 | ||||||
| export interface NavigationExtras { relativeTo?: ActivatedRoute; queryParameters?: Params; fragment?: string; } | export interface NavigationExtras { relativeTo?: ActivatedRoute; queryParameters?: Params; fragment?: string; } | ||||||
| 
 | 
 | ||||||
| @ -27,6 +26,7 @@ export interface NavigationExtras { relativeTo?: ActivatedRoute; queryParameters | |||||||
| export class Router { | export class Router { | ||||||
|   private currentUrlTree: UrlTree; |   private currentUrlTree: UrlTree; | ||||||
|   private currentRouterState: RouterState; |   private currentRouterState: RouterState; | ||||||
|  |   private currentRouterStateCandidate: RouterStateCandidate; | ||||||
|   private config: RouterConfig; |   private config: RouterConfig; | ||||||
|   private locationSubscription: Subscription; |   private locationSubscription: Subscription; | ||||||
| 
 | 
 | ||||||
| @ -36,6 +36,7 @@ export class Router { | |||||||
|   constructor(private rootComponentType:Type, private resolver: ComponentResolver, private urlSerializer: UrlSerializer, private outletMap: RouterOutletMap, private location: Location) { |   constructor(private rootComponentType:Type, private resolver: ComponentResolver, private urlSerializer: UrlSerializer, private outletMap: RouterOutletMap, private location: Location) { | ||||||
|     this.currentUrlTree = createEmptyUrlTree(); |     this.currentUrlTree = createEmptyUrlTree(); | ||||||
|     this.currentRouterState = createEmptyState(rootComponentType); |     this.currentRouterState = createEmptyState(rootComponentType); | ||||||
|  |     this.currentRouterStateCandidate = createEmptyStateCandidate(rootComponentType); | ||||||
|     this.setUpLocationChangeListener(); |     this.setUpLocationChangeListener(); | ||||||
|     this.navigateByUrl(this.location.path()); |     this.navigateByUrl(this.location.path()); | ||||||
|   } |   } | ||||||
| @ -159,66 +160,87 @@ export class Router { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private runNavigate(url:UrlTree, pop?:boolean):Observable<void> { |   private runNavigate(url:UrlTree, pop?:boolean):Observable<void> { | ||||||
|     const r = recognize(this.config, url, this.currentRouterState).mergeMap((newState:RouterState) => { |     let candidate; | ||||||
|       return new ActivateRoutes(this.resolver, newState, this.currentRouterState).activate(this.outletMap).map(() => { |     let state; | ||||||
|         this.currentUrlTree = url; |     const r = recognize(this.rootComponentType, this.config, url).mergeMap((newRouterStateCandidate) => { | ||||||
|         this.currentRouterState = newState; |       return resolve(this.resolver, newRouterStateCandidate); | ||||||
|         if (!pop) { | 
 | ||||||
|           this.location.go(this.urlSerializer.serialize(url)); |     }).map((routerStateCandidate) => { | ||||||
|         } |       candidate = routerStateCandidate; | ||||||
|       }); |       return createRouterState(routerStateCandidate, this.currentRouterStateCandidate, this.currentRouterState); | ||||||
|  | 
 | ||||||
|  |     }).map((newState:RouterState) => { | ||||||
|  |       state = newState; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     r.subscribe((_) => { | ||||||
|  |       new ActivateRoutes(state, this.currentRouterState, candidate).activate(this.outletMap); | ||||||
|  | 
 | ||||||
|  |       this.currentUrlTree = url; | ||||||
|  |       this.currentRouterState = state; | ||||||
|  |       this.currentRouterStateCandidate = candidate; | ||||||
|  | 
 | ||||||
|  |       if (!pop) { | ||||||
|  |         this.location.go(this.urlSerializer.serialize(url)); | ||||||
|  |       } | ||||||
|     }); |     }); | ||||||
|     r.subscribe((a) => {}, (e) => {}, () => {}); // force execution
 |  | ||||||
|     return r; |     return r; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ActivateRoutes { | class ActivateRoutes { | ||||||
|   constructor(private resolver: ComponentResolver, private futureState: RouterState, private currState: RouterState) {} |   constructor(private futureState: RouterState, private currState: RouterState, | ||||||
|  |               private futureStateCandidate: RouterStateCandidate) {} | ||||||
| 
 | 
 | ||||||
|   activate(parentOutletMap: RouterOutletMap): Observable<void> { |   activate(parentOutletMap: RouterOutletMap):void { | ||||||
|     const currRoot = this.currState ? rootNode(this.currState) : null; |     const futureRoot = this.futureState._root; | ||||||
|     const futureRoot = rootNode(this.futureState); |     const currRoot = this.currState ? this.currState._root : null; | ||||||
|     return this.activateChildRoutes(futureRoot, currRoot, parentOutletMap); |     const futureCandidateRoot = this.futureStateCandidate._root; | ||||||
|  |     this.activateChildRoutes(futureRoot, currRoot, futureCandidateRoot, parentOutletMap); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private activateChildRoutes(futureNode: TreeNode<ActivatedRoute>, |   private activateChildRoutes(futureNode: TreeNode<ActivatedRoute>, | ||||||
|                               currNode: TreeNode<ActivatedRoute> | null, |                               currNode: TreeNode<ActivatedRoute> | null, | ||||||
|                               outletMap: RouterOutletMap): Observable<any> { |                               futureCandidateNode: TreeNode<ActivatedRouteCandidate>, | ||||||
|  |                               outletMap: RouterOutletMap): void { | ||||||
|     const prevChildren = nodeChildrenAsMap(currNode); |     const prevChildren = nodeChildrenAsMap(currNode); | ||||||
|     const observables = []; |     for (let i = 0; i < futureNode.children.length; ++i) { | ||||||
|     futureNode.children.forEach(c => { |       const c = futureNode.children[i]; | ||||||
|       observables.push(this.activateRoutes(c, prevChildren[c.value.outlet], outletMap).toPromise()); |       const cc = futureCandidateNode.children[i]; | ||||||
|  |       this.activateRoutes(c, prevChildren[c.value.outlet], cc, outletMap); | ||||||
|       delete prevChildren[c.value.outlet]; |       delete prevChildren[c.value.outlet]; | ||||||
|     }); |     } | ||||||
|     forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k])); |     forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k])); | ||||||
|     return forkJoin(observables); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   activateRoutes(futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute>, |   activateRoutes(futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute>, | ||||||
|                  parentOutletMap: RouterOutletMap): Observable<void> { |                  futureCandidateNode: TreeNode<ActivatedRouteCandidate>, | ||||||
|  |                  parentOutletMap: RouterOutletMap): void { | ||||||
|     const future = futureNode.value; |     const future = futureNode.value; | ||||||
|  |     const futureCandidate = futureCandidateNode.value; | ||||||
|     const curr = currNode ? currNode.value : null; |     const curr = currNode ? currNode.value : null; | ||||||
|     const outlet = getOutlet(parentOutletMap, futureNode.value); |     const outlet = getOutlet(parentOutletMap, futureNode.value); | ||||||
| 
 | 
 | ||||||
|     if (future === curr) { |     if (future === curr) { | ||||||
|       return this.activateChildRoutes(futureNode, currNode, outlet.outletMap); |       pushValues(future, futureCandidate); | ||||||
|  |       this.activateChildRoutes(futureNode, currNode, futureCandidateNode, outlet.outletMap); | ||||||
|     } else { |     } else { | ||||||
|       this.deactivateOutletAndItChildren(outlet); |       this.deactivateOutletAndItChildren(outlet); | ||||||
|       const outletMap = new RouterOutletMap(); |       const outletMap = new RouterOutletMap(); | ||||||
|       return this.activateNewRoutes(outletMap, future, outlet).mergeMap(() => |       this.activateNewRoutes(outletMap, future, futureCandidate, outlet); | ||||||
|         this.activateChildRoutes(futureNode, currNode, outletMap)); |       this.activateChildRoutes(futureNode, currNode, futureCandidateNode, outletMap); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private activateNewRoutes(outletMap: RouterOutletMap, future: ActivatedRoute, outlet: RouterOutlet): Observable<void> { |   private activateNewRoutes(outletMap: RouterOutletMap, future: ActivatedRoute, | ||||||
|  |                             futureCandidate: ActivatedRouteCandidate, outlet: RouterOutlet): void { | ||||||
|     const resolved = ReflectiveInjector.resolve([ |     const resolved = ReflectiveInjector.resolve([ | ||||||
|       {provide: ActivatedRoute, useValue: future}, |       {provide: ActivatedRoute, useValue: future}, | ||||||
|       {provide: RouterOutletMap, useValue: outletMap} |       {provide: RouterOutletMap, useValue: outletMap} | ||||||
|     ]); |     ]); | ||||||
|     return fromPromise(this.resolver.resolveComponent(<any>future.component)). |     outlet.activate(futureCandidate._resolvedComponentFactory, resolved, outletMap); | ||||||
|       map(factory => outlet.activate(factory, resolved, outletMap)); |     pushValues(future, futureCandidate); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private deactivateOutletAndItChildren(outlet: RouterOutlet): void { |   private deactivateOutletAndItChildren(outlet: RouterOutlet): void { | ||||||
| @ -229,6 +251,11 @@ class ActivateRoutes { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function pushValues(route: ActivatedRoute, candidate: ActivatedRouteCandidate): void { | ||||||
|  |   (<any>route.urlSegments).next(candidate.urlSegments); | ||||||
|  |   (<any>route.params).next(candidate.params); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function nodeChildrenAsMap(node: TreeNode<ActivatedRoute>|null) { | function nodeChildrenAsMap(node: TreeNode<ActivatedRoute>|null) { | ||||||
|   return node ? |   return node ? | ||||||
|     node.children.reduce( |     node.children.reduce( | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user