fix(platform-browser): should not throw for debug attrs containing $ (#14353)
Closes #9566 PR Close #14353
This commit is contained in:
parent
e5a144d902
commit
1cfbefebe3
|
@ -1507,6 +1507,14 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||||
.toContain('ng-reflect-dir-prop="hello"');
|
.toContain('ng-reflect-dir-prop="hello"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should work with prop names containing '$'`, () => {
|
||||||
|
TestBed.configureTestingModule({declarations: [ParentCmp, SomeCmpWithInput]});
|
||||||
|
const fixture = TestBed.createComponent(ParentCmp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test$="hello"');
|
||||||
|
});
|
||||||
|
|
||||||
it('should reflect property values on template comments', () => {
|
it('should reflect property values on template comments', () => {
|
||||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||||
const template = '<template [ngIf]="ctxBoolProp"></template>';
|
const template = '<template [ngIf]="ctxBoolProp"></template>';
|
||||||
|
@ -2300,3 +2308,16 @@ class DirectiveWithPropDecorators {
|
||||||
class SomeCmp {
|
class SomeCmp {
|
||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'parent-cmp',
|
||||||
|
template: `<cmp [test$]="name"></cmp>`,
|
||||||
|
})
|
||||||
|
export class ParentCmp {
|
||||||
|
name: string = 'hello';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'cmp', template: ''})
|
||||||
|
class SomeCmpWithInput {
|
||||||
|
@Input() test$: any;
|
||||||
|
}
|
||||||
|
|
|
@ -230,7 +230,14 @@ export class DomRenderer implements Renderer {
|
||||||
renderElement.nodeValue =
|
renderElement.nodeValue =
|
||||||
TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(parsedBindings, null, 2));
|
TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(parsedBindings, null, 2));
|
||||||
} else {
|
} else {
|
||||||
this.setElementAttribute(renderElement, propertyName, propertyValue);
|
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
|
||||||
|
if (propertyName[propertyName.length - 1] === '$') {
|
||||||
|
const attrNode: Attr = createAttrNode(propertyName).cloneNode(true) as Attr;
|
||||||
|
attrNode.value = propertyValue;
|
||||||
|
renderElement.setAttributeNode(attrNode);
|
||||||
|
} else {
|
||||||
|
this.setElementAttribute(renderElement, propertyName, propertyValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,3 +348,20 @@ export function splitNamespace(name: string): string[] {
|
||||||
const match = name.match(NS_PREFIX_RE);
|
const match = name.match(NS_PREFIX_RE);
|
||||||
return [match[1], match[2]];
|
return [match[1], match[2]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let attrCache: Map<string, Attr>;
|
||||||
|
|
||||||
|
function createAttrNode(name: string): Attr {
|
||||||
|
if (!attrCache) {
|
||||||
|
attrCache = new Map<string, Attr>();
|
||||||
|
}
|
||||||
|
if (attrCache.has(name)) {
|
||||||
|
return attrCache.get(name);
|
||||||
|
} else {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = `<div ${name}>`;
|
||||||
|
const attr: Attr = div.firstChild.attributes[0];
|
||||||
|
attrCache.set(name, attr);
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue