From a7f0d7e62b5a660429a839874baca522e0f330e5 Mon Sep 17 00:00:00 2001 From: Haralan Dobrev Date: Tue, 24 Jul 2012 16:10:24 +0300 Subject: [PATCH] $route and ngView supports multiple named views Usage and purpose ================= Routes can have a view attached via the `view` property. Views can be named like so:
Then you could create a route which will update only this view: $routeProvider.when('/somepath', { templateUrl: '/section-template.html', view: 'subsection' }); When the url is changed to '/somepath' only views named 'subsection' would be updated. This should be used for: - subsections on the page which should respond to some routes, other views would not be changed - modal dialogs with their own url, like Pinterest, Trello etc. Different Cases =============== - a view has a name, but the route does not have a view set * the view is not updated * the route is considered to be a main route * the view is a secondary view. - the route has a `view` property, but a view is not named * the view is not named * the route is considered to be secondary * the view is (the) main view and would update only for main routes - the route has a `view` property and the view is named, but the name does not match * the view is not updated * the view is secondary * the route is secondary - it propably updates another view - the route has a `view` property and the view is named with the same * the view is updated regarding the template and the controller associated with the route * the route and view are secondary, but they are bound together If the location is changed to a route which has a view attached, but such view does not exist then AngularJS will do nothing and no view will be updated. Implementation ============== The whole thing is happening in the `ngView` directive on `$routeChangeSuccess` event. When the `update` function in `ngView` is called the view is updated only if the current route and the view match. `$route.last` is set to the previous route when a named view is updated. This could be used from a developer to recover the location to a previous path corresponding to a main route. For example if you have a modal dialog with its own url and a matching route, when the dialog is closed you would want to recover the url to the location of the previous main route. `$route.interpolate()` is exposed so it could be used in such scenario to build the path using `$route.last.params`. --- src/ng/directive/ngView.js | 30 +++++++++++++++++-- src/ng/route.js | 60 +++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/ng/directive/ngView.js b/src/ng/directive/ngView.js index 6bfbb279ab6e..f851478b1a52 100644 --- a/src/ng/directive/ngView.js +++ b/src/ng/directive/ngView.js @@ -131,16 +131,40 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c destroyLastScope(); } - function update() { - var locals = $route.current && $route.current.locals, + function update(event, next, last) { + var currentRoute = $route.current, + locals = currentRoute && currentRoute.locals, template = locals && locals.$template; + if ( ! currentRoute && attr.ngView) { + return; + } + + if (currentRoute && currentRoute.$route.view && ! attr.ngView) { + return; + } + + if (attr.ngView && currentRoute && ! currentRoute.$route.view) { + scope.$broadcast('$viewContentCleared'); + clearContent(); + } + + if (currentRoute && attr.ngView && ( ! currentRoute.$route.view || attr.ngView !== currentRoute.$route.view)) { + return; + } + + if (last && attr.ngView && (last.$route.templateUrl != next.$route.templateUrl || last.$route.view != next.$route.view)) { + $route.last = last; + } else { + $route.last = null; + } + if (template) { element.html(template); destroyLastScope(); var link = $compile(element.contents()), - current = $route.current, + current = currentRoute, controller; lastScope = current.scope = scope.$new(); diff --git a/src/ng/route.js b/src/ng/route.js index 298732aac0b0..5b005c4bcf54 100644 --- a/src/ng/route.js +++ b/src/ng/route.js @@ -65,6 +65,10 @@ function $RouteProvider(){ * If the option is set to `false` and url in the browser changes, then * `$routeUpdate` event is broadcasted on the root scope. * + * - `view` - `{string}`: a name of the view associated with the route. + * Useful for multiple views. If no view is specified then all of the views + * with no name are affected from the $routeChangeSuccess event. + * * @returns {Object} self * * @description @@ -305,6 +309,41 @@ function $RouteProvider(){ reload: function() { forceReload = true; $rootScope.$evalAsync(updateRoute); + }, + + /** + * @ngdoc method + * @name ng.$route#interpolate + * @methodOf ng.$route + * + * @description + * + * Interpolate a path with params. + * + * Used to interpolate `redirectTo` path. + * Exposed to be used for manually getting a path + * from a route object. + * + * Example usage: + * routePath = '/path/:someParam'; + * $route.interpolate(routePath, routeObject.params); + * + * @returns interpolation of a path with the parameters + */ + interpolate: function(string, params) { + var result = []; + forEach((string||'').split(':'), function(segment, i) { + if (i === 0) { + result.push(segment); + } else { + var segmentMatch = segment.match(/(\w+)(.*)/); + var key = segmentMatch[1]; + result.push(params[key]); + result.push(segmentMatch[2] || ''); + delete params[key]; + } + }); + return result.join(''); } }; @@ -354,7 +393,7 @@ function $RouteProvider(){ if (next) { if (next.redirectTo) { if (isString(next.redirectTo)) { - $location.path(interpolate(next.redirectTo, next.params)).search(next.params) + $location.path($route.interpolate(next.redirectTo, next.params)).search(next.params) .replace(); } else { $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) @@ -427,24 +466,5 @@ function $RouteProvider(){ // No route matched; fallback to "otherwise" route return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); } - - /** - * @returns interpolation of the redirect path with the parametrs - */ - function interpolate(string, params) { - var result = []; - forEach((string||'').split(':'), function(segment, i) { - if (i == 0) { - result.push(segment); - } else { - var segmentMatch = segment.match(/(\w+)(.*)/); - var key = segmentMatch[1]; - result.push(params[key]); - result.push(segmentMatch[2] || ''); - delete params[key]; - } - }); - return result.join(''); - } }]; }