Skip to content

Commit e942709

Browse files
committed
feat(compiler): Support $baseUrl in HTML attributes when loading a template.
Angular fetches template HTML files outside of the browser's normal parsing flow. As a result, URLs in template files are interpreted relative to the root application, when the components defined by the template files are inserted into the DOM. This change enables a template author to prefix URLs with the string $baseUrl, which will be replaced with the relative base path of the template file. So for an example template loaded from /component/foo/template.html: <img src="http://github.com/$baseUrl/logo.png" /> becomes: <img src="http://github.com/component/foo/logo.png" /> Addresses angular#2384.
1 parent 40d21b8 commit e942709

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

modules/angular2/src/render/dom/compiler/view_loader.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import {Injectable} from 'angular2/di';
2-
import {isBlank, isPresent, BaseException, stringify, isPromise} from 'angular2/src/facade/lang';
2+
import {
3+
isBlank,
4+
isPresent,
5+
BaseException,
6+
stringify,
7+
isPromise,
8+
StringWrapper
9+
} from 'angular2/src/facade/lang';
310
import {Map, MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
411
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
512
import {DOM} from 'angular2/src/dom/dom_adapter';
@@ -82,9 +89,17 @@ export class ViewLoader {
8289
throw new BaseException('View should have either the templateUrl or template property set');
8390
}
8491

85-
// Inline the style tags from the html
8692
return html.then(html => {
8793
var tplEl = DOM.createTemplate(html);
94+
95+
// Replace $baseUrl with the base url for the template
96+
let templateAbsUrl = view.templateAbsUrl;
97+
if (isPresent(templateAbsUrl) && templateAbsUrl.indexOf("/") >= 0) {
98+
let baseUrl = templateAbsUrl.substring(0, templateAbsUrl.lastIndexOf("/"));
99+
this._substituteBaseUrl(DOM.content(tplEl), baseUrl);
100+
}
101+
102+
// Inline the style tags from the html
88103
let styleEls = DOM.querySelectorAll(DOM.content(tplEl), 'STYLE');
89104

90105
let promises: List<Promise<string>> = [];
@@ -99,6 +114,31 @@ export class ViewLoader {
99114
});
100115
}
101116

117+
/**
118+
* Replace all occurrences of $baseUrl in the attributes of an element and its
119+
* children with the base URL of the template.
120+
*
121+
* @param element The element to process
122+
* @param baseUrl The base URL of the template.
123+
* @private
124+
*/
125+
private _substituteBaseUrl(element, baseUrl: string): void {
126+
if (DOM.isElementNode(element)) {
127+
var attrs = DOM.attributeMap(element);
128+
MapWrapper.forEach(attrs, (v, k) => {
129+
if (isPresent(v) && v.indexOf('$baseUrl') >= 0) {
130+
DOM.setAttribute(element, k, StringWrapper.replaceAll(v, /\$baseUrl/g, baseUrl));
131+
}
132+
});
133+
}
134+
let children = DOM.childNodes(element);
135+
for (let i = 0; i < children.length; i++) {
136+
if (DOM.isElementNode(children[i])) {
137+
this._substituteBaseUrl(children[i], baseUrl);
138+
}
139+
}
140+
}
141+
102142
/**
103143
* Inlines a style element.
104144
*

modules/angular2/test/render/dom/compiler/view_loader_spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ export function main() {
118118
});
119119
xhr.flush();
120120
}));
121+
it('should replace $baseUrl in attributes with the template base url',
122+
inject([AsyncTestCompleter], (async) => {
123+
xhr.expect('http://ng.io/path/foo.html', '<img src="$baseUrl/logo.png">');
124+
var view = new ViewDefinition({templateAbsUrl: 'http://ng.io/path/foo.html'});
125+
loader.load(view).then((el) => {
126+
expect(DOM.getInnerHTML(el)).toEqual('<img src="http://ng.io/path/logo.png">');
127+
async.done();
128+
});
129+
xhr.flush();
130+
}));
121131
});
122132

123133
describe('css', () => {

0 commit comments

Comments
 (0)