5
5
* @ngdoc service
6
6
* @name angular.service.$route
7
7
* @requires $location
8
+ * @requires $routeParams
8
9
*
9
10
* @property {Object } current Reference to the current route definition.
10
11
* @property {Array.<Object> } routes Array of all configured routes.
14
15
* definition. It is used for deep-linking URLs to controllers and views (HTML partials).
15
16
*
16
17
* The `$route` service is typically used in conjunction with {@link angular.widget.ng:view ng:view}
17
- * widget.
18
+ * widget and the { @link angular.service.$routeParams $routeParams} service .
18
19
*
19
20
* @example
20
21
This example shows how changing the URL hash causes the <tt>$route</tt>
24
25
<doc:example>
25
26
<doc:source jsfiddle="false">
26
27
<script>
27
- function MainCntl($route, $location) {
28
+ function MainCntl($route, $routeParams, $ location) {
28
29
this.$route = $route;
29
30
this.$location = $location;
31
+ this.$routeParams = $routeParams;
30
32
31
33
$route.when('/Book/:bookId', {template: 'examples/book.html', controller: BookCntl});
32
34
$route.when('/Book/:bookId/ch/:chapterId', {template: 'examples/chapter.html', controller: ChapterCntl});
33
- $route.onChange(function() {
34
- $route.current.scope.params = $route.current.params;
35
- });
36
35
}
37
36
38
- function BookCntl() {
37
+ function BookCntl($routeParams ) {
39
38
this.name = "BookCntl";
39
+ this.params = $routeParams;
40
40
}
41
41
42
- function ChapterCntl() {
42
+ function ChapterCntl($routeParams ) {
43
43
this.name = "ChapterCntl";
44
+ this.params = $routeParams;
44
45
}
45
46
</script>
46
47
54
55
<pre>$route.current.template = {{$route.current.template}}</pre>
55
56
<pre>$route.current.params = {{$route.current.params}}</pre>
56
57
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
58
+ <pre>$routeParams = {{$routeParams}}</pre>
57
59
<hr />
58
60
<ng:view></ng:view>
59
61
</div>
62
64
</doc:scenario>
63
65
</doc:example>
64
66
*/
65
- angularServiceInject ( '$route' , function ( $location ) {
67
+ angularServiceInject ( '$route' , function ( $location , $routeParams ) {
68
+ /**
69
+ * @workInProgress
70
+ * @ngdoc event
71
+ * @name angular.service.$route#$beforeRouteChange
72
+ * @eventOf angular.service.$route
73
+ * @eventType Broadcast on root scope
74
+ * @description
75
+ * Broadcasted before a route change.
76
+ *
77
+ * @param {Route } next Future route information.
78
+ * @param {Route } current Current route information.
79
+ *
80
+ * The `Route` object extends the route definition with the following properties.
81
+ *
82
+ * * `scope` - The instance of the route controller.
83
+ * * `params` - The current {@link angular.service.$routeParams params}.
84
+ *
85
+ */
86
+
87
+ /**
88
+ * @workInProgress
89
+ * @ngdoc event
90
+ * @name angular.service.$route#$afterRouteChange
91
+ * @eventOf angular.service.$route
92
+ * @eventType Broadcast on root scope
93
+ * @description
94
+ * Broadcasted after a route change.
95
+ *
96
+ * @param {Route } current Current route information.
97
+ * @param {Route } previous Previous route information.
98
+ *
99
+ * The `Route` object extends the route definition with the following properties.
100
+ *
101
+ * * `scope` - The instance of the route controller.
102
+ * * `params` - The current {@link angular.service.$routeParams params}.
103
+ *
104
+ */
105
+
106
+ /**
107
+ * @workInProgress
108
+ * @ngdoc event
109
+ * @name angular.service.$route#$routeUpdate
110
+ * @eventOf angular.service.$route
111
+ * @eventType Emit on the current route scope.
112
+ * @description
113
+ *
114
+ * The `reloadOnSearch` property has been set to false, and we are reusing the same
115
+ * instance of the Controller.
116
+ */
117
+
66
118
var routes = { } ,
67
- onChange = [ ] ,
68
119
matcher = switchRouteMatcher ,
69
120
parentScope = this ,
121
+ rootScope = this ,
70
122
dirty = 0 ,
71
- lastHashPath ,
72
- lastRouteParams ,
123
+ allowReload = true ,
73
124
$route = {
74
125
routes : routes ,
75
126
76
- /**
77
- * @workInProgress
78
- * @ngdoc method
79
- * @name angular.service.$route#onChange
80
- * @methodOf angular.service.$route
81
- *
82
- * @param {function() } fn Function that will be called when `$route.current` changes.
83
- * @returns {function() } The registered function.
84
- *
85
- * @description
86
- * Register a handler function that will be called when route changes
87
- */
88
- onChange : function ( fn ) {
89
- onChange . push ( fn ) ;
90
- return fn ;
91
- } ,
92
-
93
127
/**
94
128
* @workInProgress
95
129
* @ngdoc method
@@ -114,7 +148,7 @@ angularServiceInject('$route', function($location) {
114
148
* @methodOf angular.service.$route
115
149
*
116
150
* @param {string } path Route path (matched against `$location.hash`)
117
- * @param {Object } params Mapping information to be assigned to `$route.current` on route
151
+ * @param {Object } route Mapping information to be assigned to `$route.current` on route
118
152
* match.
119
153
*
120
154
* Object properties:
@@ -139,14 +173,15 @@ angularServiceInject('$route', function($location) {
139
173
* to update `$location.hash`.
140
174
*
141
175
* - `[reloadOnSearch=true]` - {boolean=} - reload route when $location.hashSearch
142
- * changes. If this option is disabled, you should set up a $watch to be notified of
143
- * param (hashSearch) changes as follows:
176
+ * changes.
177
+ *
178
+ * If the option is set to false and url in the browser changes, then
179
+ * $routeUpdate event is emited on the current route scope. You can use this event to
180
+ * react to {@link angular.service.$routeParams} changes:
144
181
*
145
- * function MyCtrl($route) {
146
- * this.$watch(function() {
147
- * return $route.current.params;
148
- * }, function(scope, params) {
149
- * //do stuff with params
182
+ * function MyCtrl($route, $routeParams) {
183
+ * this.$on('$routeUpdate', function() {
184
+ * // do stuff with $routeParams
150
185
* });
151
186
* }
152
187
*
@@ -155,13 +190,13 @@ angularServiceInject('$route', function($location) {
155
190
* @description
156
191
* Adds a new route definition to the `$route` service.
157
192
*/
158
- when :function ( path , params ) {
193
+ when :function ( path , route ) {
159
194
if ( isUndefined ( path ) ) return routes ; //TODO(im): remove - not needed!
160
- var route = routes [ path ] ;
161
- if ( ! route ) route = routes [ path ] = { reloadOnSearch : true } ;
162
- if ( params ) extend ( route , params ) ; //TODO(im): what the heck? merge two route definitions?
195
+ var routeDef = routes [ path ] ;
196
+ if ( ! routeDef ) routeDef = routes [ path ] = { reloadOnSearch : true } ;
197
+ if ( route ) extend ( routeDef , route ) ; //TODO(im): what the heck? merge two route definitions?
163
198
dirty ++ ;
164
- return route ;
199
+ return routeDef ;
165
200
} ,
166
201
167
202
/**
@@ -192,10 +227,18 @@ angularServiceInject('$route', function($location) {
192
227
*/
193
228
reload : function ( ) {
194
229
dirty ++ ;
230
+ allowReload = false ;
195
231
}
196
232
} ;
197
233
198
234
235
+
236
+ this . $watch ( function ( ) { return dirty + $location . hash ; } , updateRoute ) ;
237
+
238
+ return $route ;
239
+
240
+ /////////////////////////////////////////////////////
241
+
199
242
function switchRouteMatcher ( on , when , dstName ) {
200
243
var regex = '^' + when . replace ( / [ \. \\ \( \) \^ \$ ] / g, "\$1" ) + '$' ,
201
244
params = [ ] ,
@@ -219,79 +262,72 @@ angularServiceInject('$route', function($location) {
219
262
return match ? dst : null ;
220
263
}
221
264
222
-
223
265
function updateRoute ( ) {
224
- var selectedRoute , pathParams , segmentMatch , key , redir ;
266
+ var next = parseRoute ( ) ,
267
+ last = $route . current ;
225
268
226
- if ( $route . current ) {
227
- if ( ! $route . current . reloadOnSearch && ( lastHashPath == $location . hashPath ) ) {
228
- $route . current . params = extend ( $location . hashSearch , lastRouteParams ) ;
229
- return ;
230
- }
231
-
232
- if ( $route . current . scope ) {
233
- $route . current . scope . $destroy ( ) ;
269
+ if ( next && last && next . $route === last . $route
270
+ && equals ( next . pathParams , last . pathParams ) && ! next . reloadOnSearch && allowReload ) {
271
+ $route . current = next ;
272
+ copy ( next . params , $routeParams ) ;
273
+ last . scope && last . scope . $emit ( '$routeUpdate' ) ;
274
+ } else {
275
+ allowReload = true ;
276
+ rootScope . $broadcast ( '$beforeRouteChange' , next , last ) ;
277
+ last && last . scope && last . scope . $destroy ( ) ;
278
+ $route . current = next ;
279
+ if ( next ) {
280
+ if ( next . redirectTo ) {
281
+ $location . update ( isString ( next . redirectTo )
282
+ ? { hashSearch : next . params , hashPath : interpolate ( next . redirectTo , next . params ) }
283
+ : { hash : next . redirectTo ( next . pathParams ,
284
+ $location . hash , $location . hashPath , $location . hashSearch ) } ) ;
285
+ } else {
286
+ copy ( next . params , $routeParams ) ;
287
+ next . scope = parentScope . $new ( next . controller ) ;
288
+ }
234
289
}
290
+ rootScope . $broadcast ( '$afterRouteChange' , next , last ) ;
235
291
}
292
+ }
293
+
236
294
237
- lastHashPath = $location . hashPath ;
238
- $route . current = null ;
295
+ /**
296
+ * @returns the current active route, by matching it against the URL
297
+ */
298
+ function parseRoute ( ) {
239
299
// Match a route
240
- forEach ( routes , function ( rParams , rPath ) {
241
- if ( ! pathParams ) {
242
- if ( ( pathParams = matcher ( $location . hashPath , rPath ) ) ) {
243
- selectedRoute = rParams ;
244
- }
300
+ var params , match ;
301
+ forEach ( routes , function ( route , path ) {
302
+ if ( ! match && ( params = matcher ( $location . hashPath , path ) ) ) {
303
+ match = inherit ( route , {
304
+ params : extend ( { } , $location . hashSearch , params ) ,
305
+ pathParams : params } ) ;
306
+ match . $route = route ;
245
307
}
246
308
} ) ;
247
-
248
309
// No route matched; fallback to "otherwise" route
249
- selectedRoute = selectedRoute || routes [ null ] ;
250
-
251
- if ( selectedRoute ) {
252
- if ( selectedRoute . redirectTo ) {
253
- if ( isString ( selectedRoute . redirectTo ) ) {
254
- // interpolate the redirectTo string
255
- redir = { hashPath : '' ,
256
- hashSearch : extend ( { } , $location . hashSearch , pathParams ) } ;
257
-
258
- forEach ( selectedRoute . redirectTo . split ( ':' ) , function ( segment , i ) {
259
- if ( i == 0 ) {
260
- redir . hashPath += segment ;
261
- } else {
262
- segmentMatch = segment . match ( / ( \w + ) ( .* ) / ) ;
263
- key = segmentMatch [ 1 ] ;
264
- redir . hashPath += pathParams [ key ] || $location . hashSearch [ key ] ;
265
- redir . hashPath += segmentMatch [ 2 ] || '' ;
266
- delete redir . hashSearch [ key ] ;
267
- }
268
- } ) ;
269
- } else {
270
- // call custom redirectTo function
271
- redir = { hash : selectedRoute . redirectTo ( pathParams , $location . hash , $location . hashPath ,
272
- $location . hashSearch ) } ;
273
- }
310
+ return match || routes [ null ] && inherit ( routes [ null ] , { params : { } , pathParams :{ } } ) ;
311
+ }
274
312
275
- $location . update ( redir ) ;
276
- return ;
313
+ /**
314
+ * @returns interpolation of the redirect path with the parametrs
315
+ */
316
+ function interpolate ( string , params ) {
317
+ var result = [ ] ;
318
+ forEach ( ( string || '' ) . split ( ':' ) , function ( segment , i ) {
319
+ if ( i == 0 ) {
320
+ result . push ( segment ) ;
321
+ } else {
322
+ var segmentMatch = segment . match ( / ( \w + ) ( .* ) / ) ;
323
+ var key = segmentMatch [ 1 ] ;
324
+ result . push ( params [ key ] ) ;
325
+ result . push ( segmentMatch [ 2 ] || '' ) ;
326
+ delete params [ key ] ;
277
327
}
278
-
279
- $route . current = extend ( { } , selectedRoute ) ;
280
- $route . current . params = extend ( { } , $location . hashSearch , pathParams ) ;
281
- lastRouteParams = pathParams ;
282
- }
283
-
284
- //fire onChange callbacks
285
- forEach ( onChange , parentScope . $eval , parentScope ) ;
286
-
287
- // Create the scope if we have matched a route
288
- if ( $route . current ) {
289
- $route . current . scope = parentScope . $new ( $route . current . controller ) ;
290
- }
328
+ } ) ;
329
+ return result . join ( '' ) ;
291
330
}
292
331
293
332
294
- this . $watch ( function ( ) { return dirty + $location . hash ; } , updateRoute ) ;
295
-
296
- return $route ;
297
- } , [ '$location' ] ) ;
333
+ } , [ '$location' , '$routeParams' ] ) ;
0 commit comments