X Tutup
Skip to content

Commit 61cf499

Browse files
committed
fix(DomRenderer): correctly handle namespaced attributes
Closes angular#6363
1 parent f1f5b45 commit 61cf499

File tree

12 files changed

+109
-12
lines changed

12 files changed

+109
-12
lines changed

modules/angular2/src/compiler/html_parser.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlElementAst} from './html_ast';
1616
import {Injectable} from 'angular2/src/core/di';
1717
import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer';
1818
import {ParseError, ParseLocation, ParseSourceSpan} from './parse_util';
19-
import {HtmlTagDefinition, getHtmlTagDefinition, getNsPrefix} from './html_tags';
19+
import {HtmlTagDefinition, getHtmlTagDefinition, getNsPrefix, mergeNsAndName} from './html_tags';
2020

2121
export class HtmlTreeError extends ParseError {
2222
static create(elementName: string, location: ParseLocation, msg: string): HtmlTreeError {
@@ -238,10 +238,6 @@ class TreeBuilder {
238238
}
239239
}
240240

241-
function mergeNsAndName(prefix: string, localName: string): string {
242-
return isPresent(prefix) ? `@${prefix}:${localName}` : localName;
243-
}
244-
245241
function getElementFullName(prefix: string, localName: string,
246242
parentElement: HtmlElementAst): string {
247243
if (isBlank(prefix)) {

modules/angular2/src/compiler/html_tags.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,3 +420,7 @@ export function splitNsName(elementName: string): string[] {
420420
export function getNsPrefix(elementName: string): string {
421421
return splitNsName(elementName)[0];
422422
}
423+
424+
export function mergeNsAndName(prefix: string, localName: string): string {
425+
return isPresent(prefix) ? `@${prefix}:${localName}` : localName;
426+
}

modules/angular2/src/compiler/template_parser.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/cha
77
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
88
import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata';
99
import {HtmlParser} from './html_parser';
10-
import {splitNsName} from './html_tags';
10+
import {splitNsName, mergeNsAndName} from './html_tags';
1111
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
1212
import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
1313

14-
1514
import {
1615
ElementAst,
1716
BoundElementPropertyAst,
@@ -584,6 +583,12 @@ class TemplateParseVisitor implements HtmlAstVisitor {
584583
} else {
585584
if (parts[0] == ATTRIBUTE_PREFIX) {
586585
boundPropertyName = parts[1];
586+
let nsSeparatorIdx = boundPropertyName.indexOf(':');
587+
if (nsSeparatorIdx > -1) {
588+
let ns = boundPropertyName.substring(0, nsSeparatorIdx);
589+
let name = boundPropertyName.substring(nsSeparatorIdx + 1);
590+
boundPropertyName = mergeNsAndName(ns, name);
591+
}
587592
bindingType = PropertyBindingType.Attribute;
588593
} else if (parts[0] == CLASS_PREFIX) {
589594
boundPropertyName = parts[1];

modules/angular2/src/platform/browser/browser_adapter.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,15 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
365365
bool hasAttribute(Element element, String attribute) =>
366366
element.attributes.containsKey(attribute);
367367

368+
bool hasAttributeNS(Element element, String ns, String attribute) =>
369+
element.getAttributeNS(ns, attribute) != null;
370+
368371
String getAttribute(Element element, String attribute) =>
369372
element.getAttribute(attribute);
370373

374+
String getAttributeNS(Element element, String ns, String attribute) =>
375+
element.getAttributeNS(ns, attribute);
376+
371377
void setAttribute(Element element, String name, String value) {
372378
element.setAttribute(name, value);
373379
}
@@ -382,6 +388,10 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
382388
element.attributes.remove(name);
383389
}
384390

391+
void removeAttributeNS(Element element, String ns, String name) {
392+
element.getNamespacedAttributes(ns).remove(name);
393+
}
394+
385395
Node templateAwareRoot(Element el) => el is TemplateElement ? el.content : el;
386396

387397
HtmlDocument createHtmlDocument() =>

modules/angular2/src/platform/browser/browser_adapter.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,19 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
225225
return res;
226226
}
227227
hasAttribute(element, attribute: string): boolean { return element.hasAttribute(attribute); }
228+
hasAttributeNS(element, ns: string, attribute: string): boolean {
229+
return element.hasAttributeNS(ns, attribute);
230+
}
228231
getAttribute(element, attribute: string): string { return element.getAttribute(attribute); }
232+
getAttributeNS(element, ns: string, name: string): string {
233+
return element.getAttributeNS(ns, name);
234+
}
229235
setAttribute(element, name: string, value: string) { element.setAttribute(name, value); }
230236
setAttributeNS(element, ns: string, name: string, value: string) {
231237
element.setAttributeNS(ns, name, value);
232238
}
233239
removeAttribute(element, attribute: string) { element.removeAttribute(attribute); }
240+
removeAttributeNS(element, ns: string, name: string) { element.removeAttributeNS(ns, name); }
234241
templateAwareRoot(el): any { return this.isTemplateElement(el) ? this.content(el) : el; }
235242
createHtmlDocument(): HTMLDocument {
236243
return document.implementation.createHTMLDocument('fakeTitle');

modules/angular2/src/platform/dom/dom_adapter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,13 @@ export abstract class DomAdapter {
9797
abstract tagName(element): string;
9898
abstract attributeMap(element): Map<string, string>;
9999
abstract hasAttribute(element, attribute: string): boolean;
100+
abstract hasAttributeNS(element, ns: string, attribute: string): boolean;
100101
abstract getAttribute(element, attribute: string): string;
102+
abstract getAttributeNS(element, ns: string, attribute: string): string;
101103
abstract setAttribute(element, name: string, value: string);
102104
abstract setAttributeNS(element, ns: string, name: string, value: string);
103105
abstract removeAttribute(element, attribute: string);
106+
abstract removeAttributeNS(element, ns: string, attribute: string);
104107
abstract templateAwareRoot(el);
105108
abstract createHtmlDocument(): HTMLDocument;
106109
abstract defaultDoc(): HTMLDocument;

modules/angular2/src/platform/dom/dom_renderer.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,21 @@ export class DomRenderer implements Renderer {
178178
var attrNs;
179179
var nsAndName = splitNamespace(attributeName);
180180
if (isPresent(nsAndName[0])) {
181-
attributeName = nsAndName[0] + ':' + nsAndName[1];
181+
attributeName = nsAndName[1];
182182
attrNs = NAMESPACE_URIS[nsAndName[0]];
183183
}
184184
if (isPresent(attributeValue)) {
185185
if (isPresent(attrNs)) {
186186
DOM.setAttributeNS(renderElement, attrNs, attributeName, attributeValue);
187187
} else {
188-
DOM.setAttribute(renderElement, nsAndName[1], attributeValue);
188+
DOM.setAttribute(renderElement, attributeName, attributeValue);
189189
}
190190
} else {
191-
DOM.removeAttribute(renderElement, attributeName);
191+
if (isPresent(attrNs)) {
192+
DOM.removeAttributeNS(renderElement, attrNs, attributeName);
193+
} else {
194+
DOM.removeAttribute(renderElement, attributeName);
195+
}
192196
}
193197
}
194198

@@ -332,4 +336,4 @@ function splitNamespace(name: string): string[] {
332336
}
333337
let match = RegExpWrapper.firstMatch(NS_PREFIX_RE, name);
334338
return [match[1], match[2]];
335-
}
339+
}

modules/angular2/src/platform/server/abstract_html_adapter.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,21 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter {
295295
return element.attributes.keys.any((key) => '$key' == attribute);
296296
}
297297

298+
hasAttributeNS(element, String ns, String attribute) {
299+
throw 'not implemented';
300+
}
301+
298302
getAttribute(element, String attribute) {
299303
// `attributes` keys can be {@link AttributeName}s.
300304
var key = element.attributes.keys.firstWhere((key) => '$key' == attribute,
301305
orElse: () {});
302306
return element.attributes[key];
303307
}
304308

309+
getAttributeNS(element, String ns, String attribute) {
310+
throw 'not implemented';
311+
}
312+
305313
setAttribute(element, String name, String value) {
306314
element.attributes[name] = value;
307315
}
@@ -314,6 +322,10 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter {
314322
element.attributes.remove(attribute);
315323
}
316324

325+
removeAttributeNS(element, String ns, String attribute) {
326+
throw 'not implemented';
327+
}
328+
317329
templateAwareRoot(el) => el;
318330

319331
createHtmlDocument() {

modules/angular2/src/platform/server/parse5_adapter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,11 +429,13 @@ export class Parse5DomAdapter extends DomAdapter {
429429
hasAttribute(element, attribute: string): boolean {
430430
return element.attribs && element.attribs.hasOwnProperty(attribute);
431431
}
432+
hasAttributeNS(element, ns: string, attribute: string): boolean { throw 'not implemented'; }
432433
getAttribute(element, attribute: string): string {
433434
return element.attribs && element.attribs.hasOwnProperty(attribute) ?
434435
element.attribs[attribute] :
435436
null;
436437
}
438+
getAttributeNS(element, ns: string, attribute: string): string { throw 'not implemented'; }
437439
setAttribute(element, attribute: string, value: string) {
438440
if (attribute) {
439441
element.attribs[attribute] = value;
@@ -448,6 +450,7 @@ export class Parse5DomAdapter extends DomAdapter {
448450
StringMapWrapper.delete(element.attribs, attribute);
449451
}
450452
}
453+
removeAttributeNS(element, ns: string, name: string) { throw 'not implemented'; }
451454
templateAwareRoot(el): any { return this.isTemplateElement(el) ? this.content(el) : el; }
452455
createHtmlDocument(): Document {
453456
var newDoc = treeAdapter.createDocument();

modules/angular2/test/core/linker/integration_spec.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1848,7 +1848,7 @@ function declareTests() {
18481848

18491849
if (!IS_DART) {
18501850
var firstAttribute = DOM.getProperty(<Element>use, 'attributes')[0];
1851-
expect(firstAttribute.name).toEqual('xlink:href');
1851+
expect(firstAttribute.name).toEqual('href');
18521852
expect(firstAttribute.namespaceURI).toEqual('http://www.w3.org/1999/xlink');
18531853
} else {
18541854
// For Dart where '_Attr' has no instance getter 'namespaceURI'
@@ -1860,6 +1860,48 @@ function declareTests() {
18601860
}));
18611861

18621862
});
1863+
1864+
describe('attributes', () => {
1865+
1866+
it('should support attributes with namespace',
1867+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder,
1868+
async) => {
1869+
tcb.overrideView(SomeCmp, new ViewMetadata({template: '<svg:use xlink:href="#id" />'}))
1870+
.createAsync(SomeCmp)
1871+
.then((fixture) => {
1872+
let useEl = DOM.firstChild(fixture.debugElement.nativeElement);
1873+
expect(DOM.getAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href'))
1874+
.toEqual('#id');
1875+
async.done();
1876+
});
1877+
}));
1878+
1879+
it('should support binding to attributes with namespace',
1880+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder,
1881+
async) => {
1882+
tcb.overrideView(SomeCmp,
1883+
new ViewMetadata({template: '<svg:use [attr.xlink:href]="value" />'}))
1884+
.createAsync(SomeCmp)
1885+
.then((fixture) => {
1886+
let cmp = fixture.debugElement.componentInstance;
1887+
let useEl = DOM.firstChild(fixture.debugElement.nativeElement);
1888+
1889+
cmp.value = "#id";
1890+
fixture.detectChanges();
1891+
1892+
expect(DOM.getAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href'))
1893+
.toEqual('#id');
1894+
1895+
cmp.value = null;
1896+
fixture.detectChanges();
1897+
1898+
expect(DOM.hasAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href'))
1899+
.toEqual(false);
1900+
1901+
async.done();
1902+
});
1903+
}));
1904+
});
18631905
}
18641906
});
18651907
}
@@ -2439,3 +2481,8 @@ class DirectiveWithPropDecorators {
24392481

24402482
fireEvent(msg) { ObservableWrapper.callEmit(this.event, msg); }
24412483
}
2484+
2485+
@Component({selector: 'some-cmp'})
2486+
class SomeCmp {
2487+
value: any;
2488+
}

0 commit comments

Comments
 (0)
X Tutup