@@ -15,6 +15,15 @@ import {Location} from './location';
1515 * You can see the state of the router by inspecting the read-only field `router.navigating`.
1616 * This may be useful for showing a spinner, for instance.
1717 *
18+ * ## Concepts
19+ * Routers and component instances have a 1:1 correspondence.
20+ *
21+ * The router holds reference to a number of "outlets." An outlet is a placeholder that the
22+ * router dynamically fills in depending on the current URL.
23+ *
24+ * When the router navigates from a URL, it must first recognizes it and serialize it into an `Instruction`.
25+ * The router uses the `RouteRegistry` to get an `Instruction`.
26+ *
1827 * @exportedAs angular2/router
1928 */
2029export class Router {
@@ -28,19 +37,16 @@ export class Router {
2837
2938 _pipeline:Pipeline ;
3039 _registry:RouteRegistry ;
31- _outlets:Map < any , RouterOutlet > ;
32- _children:Map < any , Router > ;
40+ _outlets:Map < any , Outlet > ;
3341 _subject:EventEmitter ;
34- _location:Location ;
3542
36- constructor ( registry :RouteRegistry , pipeline :Pipeline , location :Location , parent :Router , hostComponent ) {
43+
44+ constructor ( registry :RouteRegistry , pipeline :Pipeline , parent :Router , hostComponent :any ) {
3745 this . hostComponent = hostComponent ;
3846 this . navigating = false ;
3947 this . parent = parent ;
4048 this . previousUrl = null ;
4149 this . _outlets = MapWrapper . create ( ) ;
42- this . _children = MapWrapper . create ( ) ;
43- this . _location = location ;
4450 this . _registry = registry ;
4551 this . _pipeline = pipeline ;
4652 this . _subject = new EventEmitter ( ) ;
@@ -51,33 +57,26 @@ export class Router {
5157 /**
5258 * Constructs a child router. You probably don't need to use this unless you're writing a reusable component.
5359 */
54- childRouter ( outletName = 'default' ) : Router {
55- var router = MapWrapper . get ( this . _children , outletName ) ;
56-
57- if ( isBlank ( router ) ) {
58- router = new ChildRouter ( this , outletName ) ;
59- MapWrapper . set ( this . _children , outletName , router ) ;
60- }
61-
62- return router ;
60+ childRouter ( hostComponent :any ) : Router {
61+ return new ChildRouter ( this , hostComponent ) ;
6362 }
6463
6564
6665 /**
6766 * Register an object to notify of route changes. You probably don't need to use this unless you're writing a reusable component.
6867 */
69- registerOutlet ( outlet :RouterOutlet , name : string = 'default' ) :Promise {
68+ registerOutlet ( outlet :RouterOutlet , name : string = 'default' ) : Promise {
7069 MapWrapper . set ( this . _outlets , name , outlet ) ;
7170 if ( isPresent ( this . _currentInstruction ) ) {
72- var childInstruction = this . _currentInstruction . getChildInstruction ( name ) ;
71+ var childInstruction = this . _currentInstruction . getChild ( name ) ;
7372 return outlet . activate ( childInstruction ) ;
7473 }
7574 return PromiseWrapper . resolve ( true ) ;
7675 }
7776
7877
7978 /**
80- * Update the routing configuration and trigger a navigation.
79+ * Dynamically update the routing configuration and trigger a navigation.
8180 *
8281 * # Usage
8382 *
@@ -98,7 +97,6 @@ export class Router {
9897 config ( config :any ) : Promise {
9998 if ( config instanceof List ) {
10099 config . forEach ( ( configObject ) => {
101- // TODO: this is a hack
102100 this . _registry . config ( this . hostComponent , configObject ) ;
103101 } ) ;
104102 } else {
@@ -110,6 +108,9 @@ export class Router {
110108
111109 /**
112110 * Navigate to a URL. Returns a promise that resolves to the canonical URL for the route.
111+ *
112+ * If the given URL begins with a `/`, router will navigate absolutely.
113+ * If the given URL does not begin with `/`, the router will navigate relative to this component.
113114 */
114115 navigate ( url :string ) :Promise {
115116 if ( this . navigating ) {
@@ -124,19 +125,16 @@ export class Router {
124125 return PromiseWrapper . resolve ( false ) ;
125126 }
126127
127- if ( isPresent ( this . _currentInstruction ) ) {
128+ if ( isPresent ( this . _currentInstruction ) ) {
128129 matchedInstruction . reuseComponentsFrom ( this . _currentInstruction ) ;
129130 }
130131
131- matchedInstruction . router = this ;
132132 this . _startNavigating ( ) ;
133133
134- var result = this . _pipeline . process ( matchedInstruction )
134+ var result = this . commit ( matchedInstruction )
135135 . then ( ( _ ) => {
136- this . _location . go ( matchedInstruction . matchedUrl ) ;
137- ObservableWrapper . callNext ( this . _subject , matchedInstruction . matchedUrl ) ;
136+ ObservableWrapper . callNext ( this . _subject , matchedInstruction . accumulatedUrl ) ;
138137 this . _finishNavigating ( ) ;
139- this . _currentInstruction = matchedInstruction ;
140138 } ) ;
141139
142140 PromiseWrapper . catchError ( result , ( _ ) => this . _finishNavigating ( ) ) ;
@@ -152,6 +150,7 @@ export class Router {
152150 this . navigating = false ;
153151 }
154152
153+
155154 /**
156155 * Subscribe to URL updates from the router
157156 */
@@ -160,25 +159,46 @@ export class Router {
160159 }
161160
162161
163- activateOutlets ( instruction :Instruction ) :Promise {
164- return this . _queryOutlets ( ( outlet , name ) => {
165- var childInstruction = instruction . getChildInstruction ( name ) ;
166- if ( childInstruction . reuse ) {
167- return PromiseWrapper . resolve ( true ) ;
162+ /**
163+ *
164+ */
165+ commit ( instruction :Instruction ) :Promise {
166+ this . _currentInstruction = instruction ;
167+
168+ // collect all outlets that do not have a corresponding child instruction
169+ // and remove them from the internal map of child outlets
170+ var toDeactivate = ListWrapper . create ( ) ;
171+ MapWrapper . forEach ( this . _outlets , ( outlet , outletName ) => {
172+ if ( ! instruction . hasChild ( outletName ) ) {
173+ MapWrapper . delete ( this . _outlets , outletName ) ;
174+ ListWrapper . push ( toDeactivate , outlet ) ;
168175 }
169- return outlet . activate ( childInstruction ) ;
170- } )
171- . then ( ( _ ) => instruction . mapChildrenAsync ( ( instruction , _ ) => {
172- return instruction . router . activateOutlets ( instruction ) ;
173- } ) ) ;
176+ } ) ;
177+
178+ return PromiseWrapper . all ( ListWrapper . map ( toDeactivate , ( outlet ) => outlet . deactivate ( ) ) )
179+ . then ( ( _ ) => this . activate ( instruction ) ) ;
174180 }
175181
176- traverseOutlets ( fn ) :Promise {
177- return this . _queryOutlets ( fn )
178- . then ( ( _ ) => mapObjAsync ( this . _children , ( child , _ ) => child . traverseOutlets ( fn ) ) ) ;
182+
183+ /**
184+ * Recursively remove all components contained by this router's outlets.
185+ * Calls deactivate hooks on all descendant components
186+ */
187+ deactivate ( ) :Promise {
188+ return this . _eachOutletAsync ( ( outlet ) => outlet . deactivate ) ;
179189 }
180190
181- _queryOutlets ( fn ) :Promise {
191+
192+ /**
193+ * Recursively activate.
194+ * Calls the "activate" hook on descendant components.
195+ */
196+ activate ( instruction :Instruction ) :Promise {
197+ return this . _eachOutletAsync ( ( outlet , name ) => outlet . activate ( instruction . getChild ( name ) ) ) ;
198+ }
199+
200+
201+ _eachOutletAsync ( fn ) :Promise {
182202 return mapObjAsync ( this . _outlets , fn ) ;
183203 }
184204
@@ -212,17 +232,26 @@ export class Router {
212232}
213233
214234export class RootRouter extends Router {
235+ _location :Location ;
236+
215237 constructor ( registry :RouteRegistry , pipeline :Pipeline , location :Location , hostComponent :Type ) {
216- super ( registry , pipeline , location , null , hostComponent ) ;
238+ super ( registry , pipeline , null , hostComponent ) ;
239+ this . _location = location ;
217240 this . _location . subscribe ( ( change ) => this . navigate ( change [ 'url' ] ) ) ;
218241 this . _registry . configFromComponent ( hostComponent ) ;
219242 this . navigate ( location . path ( ) ) ;
220243 }
244+
245+ commit ( instruction ) :Promise {
246+ return super . commit ( instruction ) . then ( ( _ ) => {
247+ this . _location . go ( instruction . accumulatedUrl ) ;
248+ } ) ;
249+ }
221250}
222251
223252class ChildRouter extends Router {
224253 constructor ( parent :Router , hostComponent ) {
225- super ( parent . _registry , parent . _pipeline , parent . _location , parent , hostComponent ) ;
254+ super ( parent . _registry , parent . _pipeline , parent , hostComponent ) ;
226255 this . parent = parent ;
227256 }
228257}
0 commit comments