diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ng/directive/ngView.js | 64 | ||||
| -rw-r--r-- | src/ng/route.js | 106 | 
2 files changed, 124 insertions, 46 deletions
| diff --git a/src/ng/directive/ngView.js b/src/ng/directive/ngView.js index 7c737765..4924ed1a 100644 --- a/src/ng/directive/ngView.js +++ b/src/ng/directive/ngView.js @@ -112,8 +112,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c      restrict: 'ECA',      terminal: true,      link: function(scope, element, attr) { -      var changeCounter = 0, -          lastScope, +      var lastScope,            onloadExp = attr.onload || '';        scope.$on('$afterRouteChange', update); @@ -127,43 +126,36 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c          }        } +      function clearContent() { +        element.html(''); +        destroyLastScope(); +      } +        function update() { -        var template = $route.current && $route.current.template, -            thisChangeId = ++changeCounter; - -        function clearContent() { -          // ignore callback if another route change occured since -          if (thisChangeId === changeCounter) { -            element.html(''); -            destroyLastScope(); -          } -        } +        var locals = $route.current && $route.current.locals, +            template = locals && locals.$template;          if (template) { -          $http.get(template, {cache: $templateCache}).success(function(response) { -            // ignore callback if another route change occured since -            if (thisChangeId === changeCounter) { -              element.html(response); -              destroyLastScope(); - -              var link = $compile(element.contents()), -                  current = $route.current, -                  controller; - -              lastScope = current.scope = scope.$new(); -              if (current.controller) { -                controller = $controller(current.controller, {$scope: lastScope}); -                element.contents().data('$ngControllerController', controller); -              } - -              link(lastScope); -              lastScope.$emit('$viewContentLoaded'); -              lastScope.$eval(onloadExp); - -              // $anchorScroll might listen on event... -              $anchorScroll(); -            } -          }).error(clearContent); +          element.html(template); +          destroyLastScope(); + +          var link = $compile(element.contents()), +              current = $route.current, +              controller; + +          lastScope = current.scope = scope.$new(); +          if (current.controller) { +            locals.$scope = lastScope; +            controller = $controller(current.controller, locals); +            element.contents().data('$ngControllerController', controller); +          } + +          link(lastScope); +          lastScope.$emit('$viewContentLoaded'); +          lastScope.$eval(onloadExp); + +          // $anchorScroll might listen on event... +          $anchorScroll();          } else {            clearContent();          } 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); +            } +          });        }      } | 
