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:
parent
812c231b0c
commit
4da5e9a156
|
@ -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
|
* 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
|
* 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.type === BindingType.Property) {
|
||||||
if (binding.name !== 'style' && binding.name !== 'class') {
|
if (binding.name !== 'style' && binding.name !== 'class') {
|
||||||
// A direct binding to a property.
|
// 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);
|
const assign = ts.createBinary(prop, ts.SyntaxKind.EqualsToken, expr);
|
||||||
this.scope.addStatement(ts.createStatement(assign));
|
this.scope.addStatement(ts.createStatement(assign));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -40,6 +40,11 @@ describe('type check blocks', () => {
|
||||||
expect(tcb(TEMPLATE)).toContain('ctx.a[ctx.b];');
|
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', () => {
|
it('should generate a forward element reference correctly', () => {
|
||||||
const TEMPLATE = `
|
const TEMPLATE = `
|
||||||
{{ i.value }}
|
{{ i.value }}
|
||||||
|
|
|
@ -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} = {
|
const ATTR_TO_PROP: {[name: string]: string} = {
|
||||||
'class': 'className',
|
'class': 'className',
|
||||||
'for': 'htmlFor',
|
'for': 'htmlFor',
|
||||||
|
|
Loading…
Reference in New Issue