Skip to content

Commit 368d9bf

Browse files
author
Wei Wang
committed
feat(location): add configurable attr to force html5mode link rewriting
See this discussion: angular#14959 When using html5mode, sometimes it is necessary to not use the <base> 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 <a> tag as a link that should not refresh from server, even if rewriteLinks is disabled.
1 parent 5fc9933 commit 368d9bf

File tree

3 files changed

+78
-10
lines changed

3 files changed

+78
-10
lines changed

docs/content/guide/$location.ngdoc

+14-3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ To configure the `$location` service, retrieve the
9595
`true` or `enabled:true` - see HTML5 mode<br />
9696
`false` or `enabled:false` - see Hashbang mode<br />
9797
`requireBase:true` - see Relative links<br />
98+
`rewriteLinks:true` - see Html link rewriting<br />
99+
`rewriteLinks:'string'` - see Html link rewriting<br />
98100
default: `enabled:false`
99101

100102
- **hashPrefix(prefix)**: {string}<br />
@@ -326,6 +328,18 @@ reload to the original link.
326328
- Links starting with '/' that lead to a different base path<br>
327329
Example: `<a href="/not-my-base/link">link</a>`
328330

331+
If `html5Mode.rewriteLinks` is set to `false` in the html5Mode definition object passed to
332+
`$locationProvider.html5Mode()`, the browser will perform a full page reload for every link.
333+
`html5Mode.rewriteLinks` can also be set to a string, which will enable link re-writing only on
334+
links that have the given attribute.
335+
336+
For example, if `html5Mode.rewriteLinks` is set to "internal-link":
337+
- `<a href="/some/path" internal-link>link</a>` will be rewritten
338+
- `<a href="/some/path">link</a>` will perform a full page reload
339+
340+
Note that attribute name (de)normalization does not apply here, so "internalLink" is different from
341+
"internal-link".
342+
329343

330344
### Relative links
331345

@@ -853,6 +867,3 @@ angular.module('locationExample', [])
853867
# Related API
854868

855869
* {@link ng.$location `$location` API}
856-
857-
858-

src/ng/location.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,12 @@ function $LocationProvider() {
750750
* whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
751751
* true, and a base tag is not present, an error will be thrown when `$location` is injected.
752752
* See the {@link guide/$location $location guide for more information}
753-
* - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
754-
* enables/disables url rewriting for relative links.
753+
* - **rewriteLinks** - `{boolean|String}` - (default: `true`) When html5Mode is enabled,
754+
* enables/disables url rewriting for relative links. If set to a string, url rewriting will
755+
* only happen on links with an attribute that matches the given string. For example, if set
756+
* to "internal-link", then the URL will only be rewritten for `<a internal-link>` links. Note
757+
* that attribute name (de)normalization is not happening here. So "internal-link" is
758+
* different from "internalLink".
755759
*
756760
* @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
757761
*/
@@ -769,7 +773,7 @@ function $LocationProvider() {
769773
html5Mode.requireBase = mode.requireBase;
770774
}
771775

772-
if (isBoolean(mode.rewriteLinks)) {
776+
if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) {
773777
html5Mode.rewriteLinks = mode.rewriteLinks;
774778
}
775779

@@ -866,10 +870,11 @@ function $LocationProvider() {
866870
}
867871

868872
$rootElement.on('click', function(event) {
873+
var rewriteLinks = html5Mode.rewriteLinks;
869874
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
870875
// currently we open nice url link and redirect then
871876

872-
if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return;
877+
if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return;
873878

874879
var elm = jqLite(event.target);
875880

@@ -879,6 +884,8 @@ function $LocationProvider() {
879884
if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
880885
}
881886

887+
if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return;
888+
882889
var absHref = elm.prop('href');
883890
// get the actual href attribute - see
884891
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx

test/ng/locationSpec.js

+53-3
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,43 @@ describe('$location', function() {
16011601
});
16021602

16031603

1604+
it('should rewrite links when the specified rewriteLinks attr is detected', function() {
1605+
configureTestLink({
1606+
linkHref: 'link?a#b',
1607+
attrs: 'force-rewrite',
1608+
html5Mode: {enabled: true, rewriteLinks:false},
1609+
supportHist: true
1610+
});
1611+
initService({html5Mode:{enabled: true, rewriteLinks:'force-rewrite'},supportHistory:true});
1612+
inject(
1613+
initBrowser({ url: 'http://host.com/base/index.html', basePath: '/base/index.html' }),
1614+
setupRewriteChecks(),
1615+
function($browser) {
1616+
browserTrigger(link, 'click');
1617+
expectRewriteTo($browser, 'http://host.com/base/link?a#b');
1618+
}
1619+
);
1620+
});
1621+
1622+
1623+
it('should not rewrite links when the specified rewriteLinks attr is not detected', function() {
1624+
configureTestLink({
1625+
linkHref: 'link?a#b',
1626+
html5Mode: {enabled: true, rewriteLinks:'no-rewrite'},
1627+
supportHist: true
1628+
});
1629+
initService({html5Mode:{enabled: true, rewriteLinks:'yes-rewrite'},supportHistory:true});
1630+
inject(
1631+
initBrowser({ url: 'http://host.com/base/index.html', basePath: '/base/index.html' }),
1632+
setupRewriteChecks(),
1633+
function($browser) {
1634+
browserTrigger(link, 'click');
1635+
expectNoRewrite($browser);
1636+
}
1637+
);
1638+
});
1639+
1640+
16041641
it('should rewrite full url links to same domain and base path', function() {
16051642
configureTestLink({linkHref: 'http://host.com/base/new'});
16061643
initService({html5Mode:true,supportHistory:false,hashPrefix:'!'});
@@ -2357,19 +2394,32 @@ describe('$location', function() {
23572394
});
23582395

23592396

2360-
it('should only overwrite existing properties if values are boolean', function() {
2397+
it('should only overwrite existing properties if values are the correct type', function() {
23612398
module(function($locationProvider) {
23622399
$locationProvider.html5Mode({
23632400
enabled: 'duh',
23642401
requireBase: 'probably',
2365-
rewriteLinks: 'nope'
2402+
rewriteLinks: false
23662403
});
23672404

23682405
expect($locationProvider.html5Mode()).toEqual({
23692406
enabled: false,
23702407
requireBase: true,
2371-
rewriteLinks: true
2408+
rewriteLinks: false
2409+
});
2410+
});
2411+
2412+
inject(function() {});
2413+
});
2414+
2415+
2416+
it('should allow rewriteLinks config to be set to a string', function() {
2417+
module(function($locationProvider) {
2418+
$locationProvider.html5Mode({
2419+
rewriteLinks: 'yes-rewrite'
23722420
});
2421+
2422+
expect($locationProvider.html5Mode().rewriteLinks).toEqual('yes-rewrite');
23732423
});
23742424

23752425
inject(function() {});

0 commit comments

Comments
 (0)