diff options
| author | Misko Hevery | 2012-05-22 21:12:19 -0700 |
|---|---|---|
| committer | Misko Hevery | 2012-06-01 16:56:31 -0700 |
| commit | 885fb0dd0743859a8985c23e4d0c1855a2be711e (patch) | |
| tree | 96b03b26395d500c21e4044f7afb048c2a7cebe9 /src/ng/route.js | |
| parent | 4361efb03b79e71bf0cea92b94ff377ed718bad4 (diff) | |
| download | angular.js-885fb0dd0743859a8985c23e4d0c1855a2be711e.tar.bz2 | |
feat($route): resolve local route promises
Resolve all promises on route before we fire $afterRouteChange which then renders the ngView.
Diffstat (limited to 'src/ng/route.js')
| -rw-r--r-- | src/ng/route.js | 106 |
1 files changed, 96 insertions, 10 deletions
diff --git a/src/ng/route.js b/src/ng/route.js index fd54b1c5..5386e12a 100644 --- a/src/ng/route.js +++ b/src/ng/route.js @@ -19,7 +19,7 @@ function $RouteProvider(){ * @methodOf angular.module.ng.$routeProvider * * @param {string} path Route path (matched against `$location.path`). If `$location.path` - * contains redudant trailing slash or is missing one, the route will still match and the + * contains redundant trailing slash or is missing one, the route will still match and the * `$location.path` will be updated to add or drop the trailing slash to exacly match the * route definition. * @param {Object} route Mapping information to be assigned to `$route.current` on route @@ -32,6 +32,17 @@ function $RouteProvider(){ * - `template` – `{string=}` – path to an html template that should be used by * {@link angular.module.ng.$compileProvider.directive.ngView ngView} or * {@link angular.module.ng.$compileProvider.directive.ngInclude ngInclude} directives. + * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should + * be injected into the controller. If any of these dependencies are promises, they will be + * resolved and converted to a value before the controller is instantiated and the + * `$aftreRouteChange` event is fired. The map object is: + * + * - `key` – `{string}`: a name of a dependency to be injected into the controller. + * - `factory` - `{string|function}`: If `string` then it is an alias for a service. + * Otherwise if function, then it is {@link api/angular.module.AUTO.$injector#invoke injected} + * and the return value is treated as the dependency. If the result is a promise, it is resolved + * before its value is injected into the controller. + * * - `redirectTo` – {(string|function())=} – value to update * {@link angular.module.ng.$location $location} path with and trigger route redirection. * @@ -89,8 +100,8 @@ function $RouteProvider(){ }; - this.$get = ['$rootScope', '$location', '$routeParams', - function( $rootScope, $location, $routeParams) { + this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', + function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) { /** * @ngdoc object @@ -99,6 +110,16 @@ function $RouteProvider(){ * @requires $routeParams * * @property {Object} current Reference to the current route definition. + * The route definition contains: + * + * - `controller`: The controller constructor as define in route definition. + * - `locals`: A map of locals which is used by {@link angular.module.ng.$controller $controller} service for + * controller instantiation. The `locals` contain + * the resolved values of the `resolve` map. Additionally the `locals` also contain: + * + * - `$scope` - The current route scope. + * - `$template` - The current route template HTML. + * * @property {Array.<Object>} routes Array of all configured routes. * * @description @@ -153,7 +174,15 @@ function $RouteProvider(){ angular.module('ngView', [], function($routeProvider, $locationProvider) { $routeProvider.when('/Book/:bookId', { template: 'book.html', - controller: BookCntl + controller: BookCntl, + resolve: { + // I will cause a 1 second delay + delay: function($q, $timeout) { + var delay = $q.defer(); + $timeout(delay.resolve, 1000); + return delay.promise; + } + } }); $routeProvider.when('/Book/:bookId/ch/:chapterId', { template: 'chapter.html', @@ -190,6 +219,7 @@ function $RouteProvider(){ expect(content).toMatch(/Chapter Id\: 1/); element('a:contains("Scarlet")').click(); + sleep(2); // promises are not part of scenario waiting content = element('.doc-example-live [ng-view]').text(); expect(content).toMatch(/controller\: BookCntl/); expect(content).toMatch(/Book Id\: Scarlet/); @@ -204,7 +234,11 @@ function $RouteProvider(){ * @eventOf angular.module.ng.$route * @eventType broadcast on root scope * @description - * Broadcasted before a route change. + * Broadcasted before a route change. At this point the route services starts + * resolving all of the dependencies needed for the route change to occurs. + * Typically this involves fetching the view template as well as any dependencies + * defined in `resolve` route property. Once all of the dependencies are resolved + * `$afterRouteChange` is fired. * * @param {Route} next Future route information. * @param {Route} current Current route information. @@ -216,7 +250,9 @@ function $RouteProvider(){ * @eventOf angular.module.ng.$route * @eventType broadcast on root scope * @description - * Broadcasted after a route change. + * Broadcasted after a route dependencies are resolved. + * {@link angular.module.ng.$compileProvider.directive.ngView ngView} listens for the directive + * to instantiate the controller and render the view. * * @param {Route} current Current route information. * @param {Route} previous Previous route information. @@ -224,6 +260,19 @@ function $RouteProvider(){ /** * @ngdoc event + * @name angular.module.ng.$route#$routeChangeError + * @eventOf angular.module.ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted if any of the resolve promises are rejected. + * + * @param {Route} current Current route information. + * @param {Route} previous Previous route information. + * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. + */ + + /** + * @ngdoc event * @name angular.module.ng.$route#$routeUpdate * @eventOf angular.module.ng.$route * @eventType broadcast on root scope @@ -245,7 +294,7 @@ function $RouteProvider(){ * @methodOf angular.module.ng.$route * * @description - * Causes `$route` service to reload theR current route even if + * Causes `$route` service to reload the current route even if * {@link angular.module.ng.$location $location} hasn't changed. * * As a result of that, {@link angular.module.ng.$compileProvider.directive.ngView ngView} @@ -309,11 +358,48 @@ function $RouteProvider(){ $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) .replace(); } - } else { - copy(next.params, $routeParams); } } - $rootScope.$broadcast('$afterRouteChange', next, last); + + $q.when(next). + then(function() { + if (next) { + var keys = [], + values = []; + + forEach(next.resolve || {}, function(value, key) { + keys.push(key); + values.push(isFunction(value) ? $injector.invoke(value) : $injector.get(value)); + }); + if (next.template) { + keys.push('$template'); + values.push($http. + get(next.template, {cache: $templateCache}). + then(function(response) { return response.data; })); + } + return $q.all(values).then(function(values) { + var locals = {}; + forEach(values, function(value, index) { + locals[keys[index]] = value; + }); + return locals; + }); + } + }). + // after route change + then(function(locals) { + if (next == $route.current) { + if (next) { + next.locals = locals; + copy(next.params, $routeParams); + } + $rootScope.$broadcast('$afterRouteChange', next, last); + } + }, function(error) { + if (next == $route.current) { + $rootScope.$broadcast('$routeChangeError', next, last, error); + } + }); } } |
