From 5eae33d12cabad237cb6d8d5d7fb7077d88c5ec2 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 1 Aug 2016 19:14:20 -0400 Subject: [PATCH] feat(location): add configurable attr to force html5mode link rewriting See this discussion: https://github.com/angular/angular.js/issues/14959 When using html5mode, sometimes it is necessary to not use the tag in order to support SVG icons. An example of this is in the discussion linked above. When we do this, sometimes we also want to disable "rewriteLinks" so that we can still leave the Angular application via navigation. This feature allows the user to explicitly mark an tag as a link that should not refresh from server, even if rewriteLinks is disabled. --- docs/content/guide/$location.ngdoc | 17 +++++++++-- src/ng/location.js | 15 ++++++--- test/ng/locationSpec.js | 49 +++++++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/docs/content/guide/$location.ngdoc b/docs/content/guide/$location.ngdoc index db121430cd24..4a5589750dcc 100644 --- a/docs/content/guide/$location.ngdoc +++ b/docs/content/guide/$location.ngdoc @@ -95,6 +95,8 @@ To configure the `$location` service, retrieve the `true` or `enabled:true` - see HTML5 mode
`false` or `enabled:false` - see Hashbang mode
`requireBase:true` - see Relative links
+ `rewriteLinks:true` - see Html link rewriting
+ `rewriteLinks:'string'` - see Html link rewriting
default: `enabled:false` - **hashPrefix(prefix)**: {string}
@@ -326,6 +328,18 @@ reload to the original link. - Links starting with '/' that lead to a different base path
Example: `
link` +If `html5Mode.rewriteLinks` is set to `false` in the html5Mode definition object passed to +`$locationProvider.html5Mode()`, the browser will perform a full page reload for every link. +`html5Mode.rewriteLinks` can also be set to a string, which will enable link re-writing only on +links that have the given attribute. + +For example, if `html5Mode.rewriteLinks` is set to "internal-link": +- `link` will be rewritten +- `link` will perform a full page reload + +Note that attribute name (de)normalization does not apply here, so "internalLink" is different from +"internal-link". + ### Relative links @@ -853,6 +867,3 @@ angular.module('locationExample', []) # Related API * {@link ng.$location `$location` API} - - - diff --git a/src/ng/location.js b/src/ng/location.js index a9d04002dfb4..80c2fc4f5c3c 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -750,8 +750,12 @@ function $LocationProvider() { * whether or not a tag is required to be present. If `enabled` and `requireBase` are * true, and a base tag is not present, an error will be thrown when `$location` is injected. * See the {@link guide/$location $location guide for more information} - * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled, - * enables/disables url rewriting for relative links. + * - **rewriteLinks** - `{boolean|String}` - (default: `true`) When html5Mode is enabled, + * enables/disables url rewriting for relative links. If set to a string, url rewriting will + * only happen on links with an attribute that matches the given string. For example, if set + * to "internal-link", then the URL will only be rewritten for `` links. Note + * that attribute name (de)normalization is not happening here. So "internal-link" is + * different from "internalLink". * * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter */ @@ -769,7 +773,7 @@ function $LocationProvider() { html5Mode.requireBase = mode.requireBase; } - if (isBoolean(mode.rewriteLinks)) { + if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) { html5Mode.rewriteLinks = mode.rewriteLinks; } @@ -866,10 +870,11 @@ function $LocationProvider() { } $rootElement.on('click', function(event) { + var rewriteLinks = html5Mode.rewriteLinks; // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) // currently we open nice url link and redirect then - if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return; + if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return; var elm = jqLite(event.target); @@ -879,6 +884,8 @@ function $LocationProvider() { if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; } + if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return; + var absHref = elm.prop('href'); // get the actual href attribute - see // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index 20567017c5c7..00b342e76f1c 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -1588,7 +1588,7 @@ describe('$location', function() { it('should not rewrite links when rewriting links is disabled', function() { - configureTestLink({linkHref: 'link?a#b', html5Mode: {enabled: true, rewriteLinks:false}, supportHist: true}); + configureTestLink({linkHref: 'link?a#b'}); initService({html5Mode:{enabled: true, rewriteLinks:false},supportHistory:true}); inject( initBrowser({ url: 'http://host.com/base/index.html', basePath: '/base/index.html' }), @@ -1601,6 +1601,34 @@ describe('$location', function() { }); + it('should rewrite links when the specified rewriteLinks attr is detected', function() { + configureTestLink({linkHref: 'link?a#b', attrs: 'force-rewrite'}); + initService({html5Mode:{enabled: true, rewriteLinks:'force-rewrite'},supportHistory:true}); + inject( + initBrowser({ url: 'http://host.com/base/index.html', basePath: '/base/index.html' }), + setupRewriteChecks(), + function($browser) { + browserTrigger(link, 'click'); + expectRewriteTo($browser, 'http://host.com/base/link?a#b'); + } + ); + }); + + + it('should not rewrite links when the specified rewriteLinks attr is not detected', function() { + configureTestLink({linkHref: 'link?a#b'}); + initService({html5Mode:{enabled: true, rewriteLinks:'yes-rewrite'},supportHistory:true}); + inject( + initBrowser({ url: 'http://host.com/base/index.html', basePath: '/base/index.html' }), + setupRewriteChecks(), + function($browser) { + browserTrigger(link, 'click'); + expectNoRewrite($browser); + } + ); + }); + + it('should rewrite full url links to same domain and base path', function() { configureTestLink({linkHref: 'http://host.com/base/new'}); initService({html5Mode:true,supportHistory:false,hashPrefix:'!'}); @@ -1692,7 +1720,7 @@ describe('$location', function() { it('should not rewrite when full link to different base path when history enabled on old browser', function() { - configureTestLink({linkHref: 'http://host.com/other_base/link', html5Mode: true, supportHist: false}); + configureTestLink({linkHref: 'http://host.com/other_base/link'}); inject( initBrowser({ url: 'http://host.com/base/index.html', basePath: '/base/index.html' }), setupRewriteChecks(), @@ -2357,12 +2385,12 @@ describe('$location', function() { }); - it('should only overwrite existing properties if values are boolean', function() { + it('should only overwrite existing properties if values are the correct type', function() { module(function($locationProvider) { $locationProvider.html5Mode({ enabled: 'duh', requireBase: 'probably', - rewriteLinks: 'nope' + rewriteLinks: 500 }); expect($locationProvider.html5Mode()).toEqual({ @@ -2376,6 +2404,19 @@ describe('$location', function() { }); + it('should allow rewriteLinks config to be set to a string', function() { + module(function($locationProvider) { + $locationProvider.html5Mode({ + rewriteLinks: 'yes-rewrite' + }); + + expect($locationProvider.html5Mode().rewriteLinks).toEqual('yes-rewrite'); + }); + + inject(function() {}); + }); + + it('should not set unknown input properties to html5Mode object', function() { module(function($locationProvider) { $locationProvider.html5Mode({