fix(ivy): type checking - apply attribute to property name mapping (#30675)

Some HTML attributes don't correspond to their DOM property name, in which
case the runtime will apply the appropriate transformation when assigning
a property using its attribute name. One example of this is the `for`
attribute, for which the DOM property is named `htmlFor`.

The type-checking machinery in ngtsc must also take this mapping into
account, as it generates type-check code in which unclaimed property bindings
are assigned to properties of (subtypes of) `HTMLElement`.

Fixes #30607
Fixes FW-1327

PR Close #30675
This commit is contained in:
JoostK 2019-05-26 11:45:45 +02:00 committed by Misko Hevery
parent 812c231b0c
commit 4da5e9a156
3 changed files with 24 additions and 3 deletions

View File

@ -287,6 +287,19 @@ class TcbDirectiveOp extends TcbOp {
}
}
/**
* Mapping between attributes names that don't correspond to their element property names.
* Note: this mapping has to be kept in sync with the equally named mapping in the runtime.
*/
const ATTR_TO_PROP: {[name: string]: string} = {
'class': 'className',
'for': 'htmlFor',
'formaction': 'formAction',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex',
};
/**
* A `TcbOp` which generates code to check "unclaimed inputs" - bindings on an element which were
* not attributed to any directive or component, and are instead processed against the HTML element
@ -324,7 +337,8 @@ class TcbUnclaimedInputsOp extends TcbOp {
if (binding.type === BindingType.Property) {
if (binding.name !== 'style' && binding.name !== 'class') {
// A direct binding to a property.
const prop = ts.createPropertyAccess(elId, binding.name);
const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
const prop = ts.createPropertyAccess(elId, propertyName);
const assign = ts.createBinary(prop, ts.SyntaxKind.EqualsToken, expr);
this.scope.addStatement(ts.createStatement(assign));
} else {

View File

@ -40,6 +40,11 @@ describe('type check blocks', () => {
expect(tcb(TEMPLATE)).toContain('ctx.a[ctx.b];');
});
it('should translate unclaimed bindings to their property equivalent', () => {
const TEMPLATE = `<label [for]="'test'"></label>`;
expect(tcb(TEMPLATE)).toContain('_t1.htmlFor = "test";');
});
it('should generate a forward element reference correctly', () => {
const TEMPLATE = `
{{ i.value }}

View File

@ -765,8 +765,10 @@ export function generatePropertyAliases(tNode: TNode, direction: BindingDirectio
}
/**
* Mapping between attributes names that don't correspond to their element property names.
*/
* Mapping between attributes names that don't correspond to their element property names.
* Note: this mapping has to be kept in sync with the equally named mapping in the template
* type-checking machinery of ngtsc.
*/
const ATTR_TO_PROP: {[name: string]: string} = {
'class': 'className',
'for': 'htmlFor',