11import { OnInit , provide , ReflectiveInjector , ComponentResolver } from 'angular2/core' ;
22import { RouterOutlet } from './directives/router_outlet' ;
33import { Type , isBlank , isPresent } from 'angular2/src/facade/lang' ;
4- import { EventEmitter , Observable } from 'angular2/src/facade/async' ;
4+ import { ListWrapper } from 'angular2/src/facade/collection' ;
5+ import { EventEmitter , Observable , PromiseWrapper } from 'angular2/src/facade/async' ;
56import { StringMapWrapper } from 'angular2/src/facade/collection' ;
67import { BaseException } from 'angular2/src/facade/exceptions' ;
78import { RouterUrlSerializer } from './router_url_serializer' ;
9+ import { CanDeactivate } from './interfaces' ;
810import { recognize } from './recognize' ;
911import { Location } from 'angular2/platform/common' ;
12+ import { link } from './link' ;
13+
1014import {
1115 equalSegments ,
1216 routeSegmentComponentFactory ,
@@ -32,70 +36,98 @@ export class Router {
3236
3337 private _changes : EventEmitter < void > = new EventEmitter < void > ( ) ;
3438
35- constructor ( private _componentType : Type , private _componentResolver : ComponentResolver ,
39+ constructor ( private _rootComponent : Object , private _rootComponentType : Type ,
40+ private _componentResolver : ComponentResolver ,
3641 private _urlSerializer : RouterUrlSerializer ,
3742 private _routerOutletMap : RouterOutletMap , private _location : Location ) {
3843 this . navigateByUrl ( this . _location . path ( ) ) ;
3944 }
4045
4146 get urlTree ( ) : Tree < UrlSegment > { return this . _urlTree ; }
4247
43- navigate ( url : Tree < UrlSegment > ) : Promise < void > {
48+ navigateByUrl ( url : string ) : Promise < void > {
49+ return this . _navigate ( this . _urlSerializer . parse ( url ) ) ;
50+ }
51+
52+ navigate ( changes : any [ ] , segment ?: RouteSegment ) : Promise < void > {
53+ return this . _navigate ( this . createUrlTree ( changes , segment ) ) ;
54+ }
55+
56+ private _navigate ( url : Tree < UrlSegment > ) : Promise < void > {
4457 this . _urlTree = url ;
45- return recognize ( this . _componentResolver , this . _componentType , url )
58+ return recognize ( this . _componentResolver , this . _rootComponentType , url )
4659 . then ( currTree => {
47- new _LoadSegments ( currTree , this . _prevTree ) . load ( this . _routerOutletMap ) ;
48- this . _prevTree = currTree ;
49- this . _location . go ( this . _urlSerializer . serialize ( this . _urlTree ) ) ;
50- this . _changes . emit ( null ) ;
60+ return new _LoadSegments ( currTree , this . _prevTree )
61+ . load ( this . _routerOutletMap , this . _rootComponent )
62+ . then ( _ => {
63+ this . _prevTree = currTree ;
64+ this . _location . go ( this . _urlSerializer . serialize ( this . _urlTree ) ) ;
65+ this . _changes . emit ( null ) ;
66+ } ) ;
5167 } ) ;
5268 }
5369
54- serializeUrl ( url : Tree < UrlSegment > ) : string { return this . _urlSerializer . serialize ( url ) ; }
55-
56- navigateByUrl ( url : string ) : Promise < void > {
57- return this . navigate ( this . _urlSerializer . parse ( url ) ) ;
70+ createUrlTree ( changes : any [ ] , segment ?: RouteSegment ) : Tree < UrlSegment > {
71+ if ( isPresent ( this . _prevTree ) ) {
72+ let s = isPresent ( segment ) ? segment : this . _prevTree . root ;
73+ return link ( s , this . _prevTree , this . urlTree , changes ) ;
74+ } else {
75+ return null ;
76+ }
5877 }
5978
79+ serializeUrl ( url : Tree < UrlSegment > ) : string { return this . _urlSerializer . serialize ( url ) ; }
80+
6081 get changes ( ) : Observable < void > { return this . _changes ; }
82+
83+ get routeTree ( ) : Tree < RouteSegment > { return this . _prevTree ; }
6184}
6285
86+
6387class _LoadSegments {
88+ private deactivations : Object [ ] [ ] = [ ] ;
89+ private performMutation : boolean = true ;
90+
6491 constructor ( private currTree : Tree < RouteSegment > , private prevTree : Tree < RouteSegment > ) { }
6592
66- load ( parentOutletMap : RouterOutletMap ) : void {
93+ load ( parentOutletMap : RouterOutletMap , rootComponent : Object ) : Promise < void > {
6794 let prevRoot = isPresent ( this . prevTree ) ? rootNode ( this . prevTree ) : null ;
6895 let currRoot = rootNode ( this . currTree ) ;
69- this . loadChildSegments ( currRoot , prevRoot , parentOutletMap ) ;
96+
97+ return this . canDeactivate ( currRoot , prevRoot , parentOutletMap , rootComponent )
98+ . then ( res => {
99+ this . performMutation = true ;
100+ if ( res ) {
101+ this . loadChildSegments ( currRoot , prevRoot , parentOutletMap , [ rootComponent ] ) ;
102+ }
103+ } ) ;
70104 }
71105
72- loadSegments ( currNode : TreeNode < RouteSegment > , prevNode : TreeNode < RouteSegment > ,
73- parentOutletMap : RouterOutletMap ) : void {
74- let curr = currNode . value ;
75- let prev = isPresent ( prevNode ) ? prevNode . value : null ;
76- let outlet = this . getOutlet ( parentOutletMap , currNode . value ) ;
106+ private canDeactivate ( currRoot : TreeNode < RouteSegment > , prevRoot : TreeNode < RouteSegment > ,
107+ outletMap : RouterOutletMap , rootComponent : Object ) : Promise < boolean > {
108+ this . performMutation = false ;
109+ this . loadChildSegments ( currRoot , prevRoot , outletMap , [ rootComponent ] ) ;
77110
78- if ( equalSegments ( curr , prev ) ) {
79- this . loadChildSegments ( currNode , prevNode , outlet . outletMap ) ;
80- } else {
81- let outletMap = new RouterOutletMap ( ) ;
82- this . loadNewSegment ( outletMap , curr , prev , outlet ) ;
83- this . loadChildSegments ( currNode , prevNode , outletMap ) ;
84- }
111+ let allPaths = PromiseWrapper . all ( this . deactivations . map ( r => this . checkCanDeactivatePath ( r ) ) ) ;
112+ return allPaths . then ( ( values : boolean [ ] ) => values . filter ( v => v ) . length === values . length ) ;
85113 }
86114
87- private loadNewSegment ( outletMap : RouterOutletMap , curr : RouteSegment , prev : RouteSegment ,
88- outlet : RouterOutlet ) : void {
89- let resolved = ReflectiveInjector . resolve (
90- [ provide ( RouterOutletMap , { useValue : outletMap } ) , provide ( RouteSegment , { useValue : curr } ) ] ) ;
91- let ref = outlet . load ( routeSegmentComponentFactory ( curr ) , resolved , outletMap ) ;
92- if ( hasLifecycleHook ( "routerOnActivate" , ref . instance ) ) {
93- ref . instance . routerOnActivate ( curr , prev , this . currTree , this . prevTree ) ;
115+ private checkCanDeactivatePath ( path : Object [ ] ) : Promise < boolean > {
116+ let curr = PromiseWrapper . resolve ( true ) ;
117+ for ( let p of ListWrapper . reversed ( path ) ) {
118+ curr = curr . then ( _ => {
119+ if ( hasLifecycleHook ( "routerCanDeactivate" , p ) ) {
120+ return ( < CanDeactivate > p ) . routerCanDeactivate ( this . prevTree , this . currTree ) ;
121+ } else {
122+ return _ ;
123+ }
124+ } ) ;
94125 }
126+ return curr ;
95127 }
96128
97129 private loadChildSegments ( currNode : TreeNode < RouteSegment > , prevNode : TreeNode < RouteSegment > ,
98- outletMap : RouterOutletMap ) : void {
130+ outletMap : RouterOutletMap , components : Object [ ] ) : void {
99131 let prevChildren = isPresent ( prevNode ) ?
100132 prevNode . children . reduce (
101133 ( m , c ) => {
@@ -106,11 +138,42 @@ class _LoadSegments {
106138 { } ;
107139
108140 currNode . children . forEach ( c => {
109- this . loadSegments ( c , prevChildren [ c . value . outlet ] , outletMap ) ;
141+ this . loadSegments ( c , prevChildren [ c . value . outlet ] , outletMap , components ) ;
110142 StringMapWrapper . delete ( prevChildren , c . value . outlet ) ;
111143 } ) ;
112144
113- StringMapWrapper . forEach ( prevChildren , ( v , k ) => this . unloadOutlet ( outletMap . _outlets [ k ] ) ) ;
145+ StringMapWrapper . forEach ( prevChildren ,
146+ ( v , k ) => this . unloadOutlet ( outletMap . _outlets [ k ] , components ) ) ;
147+ }
148+
149+ loadSegments ( currNode : TreeNode < RouteSegment > , prevNode : TreeNode < RouteSegment > ,
150+ parentOutletMap : RouterOutletMap , components : Object [ ] ) : void {
151+ let curr = currNode . value ;
152+ let prev = isPresent ( prevNode ) ? prevNode . value : null ;
153+ let outlet = this . getOutlet ( parentOutletMap , currNode . value ) ;
154+
155+ if ( equalSegments ( curr , prev ) ) {
156+ this . loadChildSegments ( currNode , prevNode , outlet . outletMap ,
157+ components . concat ( [ outlet . loadedComponent ] ) ) ;
158+ } else {
159+ this . unloadOutlet ( outlet , components ) ;
160+ if ( this . performMutation ) {
161+ let outletMap = new RouterOutletMap ( ) ;
162+ let loadedComponent = this . loadNewSegment ( outletMap , curr , prev , outlet ) ;
163+ this . loadChildSegments ( currNode , prevNode , outletMap , components . concat ( [ loadedComponent ] ) ) ;
164+ }
165+ }
166+ }
167+
168+ private loadNewSegment ( outletMap : RouterOutletMap , curr : RouteSegment , prev : RouteSegment ,
169+ outlet : RouterOutlet ) : Object {
170+ let resolved = ReflectiveInjector . resolve (
171+ [ provide ( RouterOutletMap , { useValue : outletMap } ) , provide ( RouteSegment , { useValue : curr } ) ] ) ;
172+ let ref = outlet . load ( routeSegmentComponentFactory ( curr ) , resolved , outletMap ) ;
173+ if ( hasLifecycleHook ( "routerOnActivate" , ref . instance ) ) {
174+ ref . instance . routerOnActivate ( curr , prev , this . currTree , this . prevTree ) ;
175+ }
176+ return ref . instance ;
114177 }
115178
116179 private getOutlet ( outletMap : RouterOutletMap , segment : RouteSegment ) : RouterOutlet {
@@ -125,8 +188,15 @@ class _LoadSegments {
125188 return outlet ;
126189 }
127190
128- private unloadOutlet ( outlet : RouterOutlet ) : void {
129- StringMapWrapper . forEach ( outlet . outletMap . _outlets , ( v , k ) => { this . unloadOutlet ( v ) ; } ) ;
130- outlet . unload ( ) ;
191+ private unloadOutlet ( outlet : RouterOutlet , components : Object [ ] ) : void {
192+ if ( outlet . isLoaded ) {
193+ StringMapWrapper . forEach ( outlet . outletMap . _outlets ,
194+ ( v , k ) => this . unloadOutlet ( v , components ) ) ;
195+ if ( this . performMutation ) {
196+ outlet . unload ( ) ;
197+ } else {
198+ this . deactivations . push ( components . concat ( [ outlet . loadedComponent ] ) ) ;
199+ }
200+ }
131201 }
132202}
0 commit comments