aboutsummaryrefslogtreecommitdiffstats
path: root/src/service/route.js
diff options
context:
space:
mode:
authorIgor Minar2011-02-15 01:12:45 -0500
committerIgor Minar2011-02-15 11:01:53 -0500
commit1777110958f76ee4be5760e36c96702223385918 (patch)
tree5aa03b246507e66877e5eac69e58e004e244d7a5 /src/service/route.js
parentd2089a16335276eecb8d81eb17332c2dff2cf1a2 (diff)
downloadangular.js-1777110958f76ee4be5760e36c96702223385918.tar.bz2
split up services into individual files
- split up services into files under src/service - split up specs into files under test/service - rewrite all specs so that they don't depend on one global forEach - get rid of obsolete code and tests in ng:switch - rename mock $log spec from "$log" to "$log mock"
Diffstat (limited to 'src/service/route.js')
-rw-r--r--src/service/route.js266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/service/route.js b/src/service/route.js
new file mode 100644
index 00000000..2de484f6
--- /dev/null
+++ b/src/service/route.js
@@ -0,0 +1,266 @@
+/**
+ * @workInProgress
+ * @ngdoc service
+ * @name angular.service.$route
+ * @requires $location
+ *
+ * @property {Object} current Reference to the current route definition.
+ * @property {Array.<Object>} routes Array of all configured routes.
+ *
+ * @description
+ * Watches `$location.hashPath` and tries to map the hash to an existing route
+ * definition. It is used for deep-linking URLs to controllers and views (HTML partials).
+ *
+ * The `$route` service is typically used in conjunction with {@link angular.widget.ng:view ng:view}
+ * widget.
+ *
+ * @example
+ This example shows how changing the URL hash causes the <tt>$route</tt>
+ to match a route against the URL, and the <tt>[[ng:include]]</tt> pulls in the partial.
+ Try changing the URL in the input box to see changes.
+
+ <doc:example>
+ <doc:source>
+ <script>
+ angular.service('myApp', function($route) {
+ $route.when('/Book/:bookId', {template:'rsrc/book.html', controller:BookCntl});
+ $route.when('/Book/:bookId/ch/:chapterId', {template:'rsrc/chapter.html', controller:ChapterCntl});
+ $route.onChange(function() {
+ $route.current.scope.params = $route.current.params;
+ });
+ }, {$inject: ['$route']});
+
+ function BookCntl() {
+ this.name = "BookCntl";
+ }
+
+ function ChapterCntl() {
+ this.name = "ChapterCntl";
+ }
+ </script>
+
+ Chose:
+ <a href="#/Book/Moby">Moby</a> |
+ <a href="#/Book/Moby/ch/1">Moby: Ch1</a> |
+ <a href="#/Book/Gatsby">Gatsby</a> |
+ <a href="#/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a><br/>
+ <input type="text" name="$location.hashPath" size="80" />
+ <pre>$location={{$location}}</pre>
+ <pre>$route.current.template={{$route.current.template}}</pre>
+ <pre>$route.current.params={{$route.current.params}}</pre>
+ <pre>$route.current.scope.name={{$route.current.scope.name}}</pre>
+ <hr/>
+ <ng:include src="$route.current.template" scope="$route.current.scope"/>
+ </doc:source>
+ <doc:scenario>
+ </doc:scenario>
+ </doc:example>
+ */
+angularServiceInject('$route', function(location, $updateView) {
+ var routes = {},
+ onChange = [],
+ matcher = switchRouteMatcher,
+ parentScope = this,
+ dirty = 0,
+ $route = {
+ routes: routes,
+
+ /**
+ * @workInProgress
+ * @ngdoc method
+ * @name angular.service.$route#onChange
+ * @methodOf angular.service.$route
+ *
+ * @param {function()} fn Function that will be called when `$route.current` changes.
+ * @returns {function()} The registered function.
+ *
+ * @description
+ * Register a handler function that will be called when route changes
+ */
+ onChange: function(fn) {
+ onChange.push(fn);
+ return fn;
+ },
+
+ /**
+ * @workInProgress
+ * @ngdoc method
+ * @name angular.service.$route#parent
+ * @methodOf angular.service.$route
+ *
+ * @param {Scope} [scope=rootScope] Scope to be used as parent for newly created
+ * `$route.current.scope` scopes.
+ *
+ * @description
+ * Sets a scope to be used as the parent scope for scopes created on route change. If not
+ * set, defaults to the root scope.
+ */
+ parent: function(scope) {
+ if (scope) parentScope = scope;
+ },
+
+ /**
+ * @workInProgress
+ * @ngdoc method
+ * @name angular.service.$route#when
+ * @methodOf angular.service.$route
+ *
+ * @param {string} path Route path (matched against `$location.hash`)
+ * @param {Object} params Mapping information to be assigned to `$route.current` on route
+ * match.
+ *
+ * Object properties:
+ *
+ * - `controller` – `{function()=}` – Controller fn that should be associated with newly
+ * created scope.
+ * - `template` – `{string=}` – path to an html template that should be used by
+ * {@link angular.widget.ng:view ng:view} or
+ * {@link angular.widget.ng:include ng:include} widgets.
+ * - `redirectTo` – {(string|function())=} – value to update
+ * {@link angular.service.$location $location} hash with and trigger route redirection.
+ *
+ * If `redirectTo` is a function, it will be called with the following parameters:
+ *
+ * - `{Object.<string>}` - route parameters extracted from the current
+ * `$location.hashPath` by applying the current route template.
+ * - `{string}` - current `$location.hash`
+ * - `{string}` - current `$location.hashPath`
+ * - `{string}` - current `$location.hashSearch`
+ *
+ * The custom `redirectTo` function is expected to return a string which will be used
+ * to update `$location.hash`.
+ *
+ * @returns {Object} route object
+ *
+ * @description
+ * Adds a new route definition to the `$route` service.
+ */
+ when:function (path, params) {
+ if (isUndefined(path)) return routes; //TODO(im): remove - not needed!
+ var route = routes[path];
+ if (!route) route = routes[path] = {};
+ if (params) extend(route, params);
+ dirty++;
+ return route;
+ },
+
+ /**
+ * @workInProgress
+ * @ngdoc method
+ * @name angular.service.$route#otherwise
+ * @methodOf angular.service.$route
+ *
+ * @description
+ * Sets route definition that will be used on route change when no other route definition
+ * is matched.
+ *
+ * @param {Object} params Mapping information to be assigned to `$route.current`.
+ */
+ otherwise: function(params) {
+ $route.when(null, params);
+ },
+
+ /**
+ * @workInProgress
+ * @ngdoc method
+ * @name angular.service.$route#reload
+ * @methodOf angular.service.$route
+ *
+ * @description
+ * Causes `$route` service to reload (and recreate the `$route.current` scope) upon the next
+ * eval even if {@link angular.service.$location $location} hasn't changed.
+ */
+ reload: function() {
+ dirty++;
+ }
+ };
+
+
+ function switchRouteMatcher(on, when, dstName) {
+ var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$',
+ params = [],
+ dst = {};
+ forEach(when.split(/\W/), function(param){
+ if (param) {
+ var paramRegExp = new RegExp(":" + param + "([\\W])");
+ if (regex.match(paramRegExp)) {
+ regex = regex.replace(paramRegExp, "([^\/]*)$1");
+ params.push(param);
+ }
+ }
+ });
+ var match = on.match(new RegExp(regex));
+ if (match) {
+ forEach(params, function(name, index){
+ dst[name] = match[index + 1];
+ });
+ if (dstName) this.$set(dstName, dst);
+ }
+ return match ? dst : _null;
+ }
+
+
+ function updateRoute(){
+ var childScope, routeParams, pathParams, segmentMatch, key, redir;
+
+ $route.current = _null;
+ forEach(routes, function(rParams, rPath) {
+ if (!pathParams) {
+ if (pathParams = matcher(location.hashPath, rPath)) {
+ routeParams = rParams;
+ }
+ }
+ });
+
+ // "otherwise" fallback
+ routeParams = routeParams || routes[_null];
+
+ if(routeParams) {
+ if (routeParams.redirectTo) {
+ if (isString(routeParams.redirectTo)) {
+ // interpolate the redirectTo string
+ redir = {hashPath: '',
+ hashSearch: extend({}, location.hashSearch, pathParams)};
+
+ forEach(routeParams.redirectTo.split(':'), function(segment, i) {
+ if (i==0) {
+ redir.hashPath += segment;
+ } else {
+ segmentMatch = segment.match(/(\w+)(.*)/);
+ key = segmentMatch[1];
+ redir.hashPath += pathParams[key] || location.hashSearch[key];
+ redir.hashPath += segmentMatch[2] || '';
+ delete redir.hashSearch[key];
+ }
+ });
+ } else {
+ // call custom redirectTo function
+ redir = {hash: routeParams.redirectTo(pathParams, location.hash, location.hashPath,
+ location.hashSearch)};
+ }
+
+ location.update(redir);
+ $updateView(); //TODO this is to work around the $location<=>$browser issues
+ return;
+ }
+
+ childScope = createScope(parentScope);
+ $route.current = extend({}, routeParams, {
+ scope: childScope,
+ params: extend({}, location.hashSearch, pathParams)
+ });
+ }
+
+ //fire onChange callbacks
+ forEach(onChange, parentScope.$tryEval);
+
+ if (childScope) {
+ childScope.$become($route.current.controller);
+ }
+ }
+
+
+ this.$watch(function(){return dirty + location.hash;}, updateRoute);
+
+ return $route;
+}, ['$location', '$updateView']);