fix(ivy): generate ng-reflect properties for i18n attributes (#32989)
Prior to this change, ng-reflect properties were not created in case an attribute was marked as translatable (for ex. `i18n-title`). This commit adds the logic to generate ng-reflect for such cases. PR Close #32989
This commit is contained in:
parent
d18289fa9c
commit
90fb5d9f7a
|
@ -18,7 +18,7 @@ import {bindingUpdated} from './bindings';
|
|||
import {attachPatchData} from './context_discovery';
|
||||
import {setDelayProjection} from './instructions/all';
|
||||
import {attachI18nOpCodesDebug} from './instructions/lview_debug';
|
||||
import {TsickleIssue1009, allocExpando, elementAttributeInternal, elementPropertyInternal, getOrCreateTNode, setInputsForProperty, textBindingInternal} from './instructions/shared';
|
||||
import {TsickleIssue1009, allocExpando, elementAttributeInternal, elementPropertyInternal, getOrCreateTNode, setInputsForProperty, setNgReflectProperties, textBindingInternal} from './instructions/shared';
|
||||
import {LContainer, NATIVE} from './interfaces/container';
|
||||
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n';
|
||||
import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from './interfaces/node';
|
||||
|
@ -1006,6 +1006,10 @@ function i18nAttributesFirstPass(lView: LView, tView: TView, index: number, valu
|
|||
const dataValue = tNode.inputs && tNode.inputs[attrName];
|
||||
if (dataValue) {
|
||||
setInputsForProperty(lView, dataValue, value);
|
||||
if (ngDevMode) {
|
||||
const element = getNativeByIndex(previousElementIndex, lView) as RElement | RComment;
|
||||
setNgReflectProperties(lView, element, tNode.type, dataValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -889,20 +889,7 @@ export function elementPropertyInternal<T>(
|
|||
setInputsForProperty(lView, dataValue, value);
|
||||
if (isComponentHost(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
|
||||
if (ngDevMode) {
|
||||
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) {
|
||||
/**
|
||||
* dataValue is an array containing runtime input or output names for the directives:
|
||||
* i+0: directive instance index
|
||||
* i+1: publicName
|
||||
* i+2: privateName
|
||||
*
|
||||
* e.g. [0, 'change', 'change-minified']
|
||||
* we want to set the reflected property with the privateName: dataValue[i+2]
|
||||
*/
|
||||
for (let i = 0; i < dataValue.length; i += 3) {
|
||||
setNgReflectProperty(lView, element, tNode.type, dataValue[i + 2] as string, value);
|
||||
}
|
||||
}
|
||||
setNgReflectProperties(lView, element, tNode.type, dataValue, value);
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Element) {
|
||||
propName = mapPropName(propName);
|
||||
|
@ -945,7 +932,7 @@ function markDirtyIfOnPush(lView: LView, viewIndex: number): void {
|
|||
}
|
||||
}
|
||||
|
||||
export function setNgReflectProperty(
|
||||
function setNgReflectProperty(
|
||||
lView: LView, element: RElement | RComment, type: TNodeType, attrName: string, value: any) {
|
||||
const renderer = lView[RENDERER];
|
||||
attrName = normalizeDebugBindingName(attrName);
|
||||
|
@ -969,6 +956,25 @@ export function setNgReflectProperty(
|
|||
}
|
||||
}
|
||||
|
||||
export function setNgReflectProperties(
|
||||
lView: LView, element: RElement | RComment, type: TNodeType, dataValue: PropertyAliasValue,
|
||||
value: any) {
|
||||
if (type === TNodeType.Element || type === TNodeType.Container) {
|
||||
/**
|
||||
* dataValue is an array containing runtime input or output names for the directives:
|
||||
* i+0: directive instance index
|
||||
* i+1: publicName
|
||||
* i+2: privateName
|
||||
*
|
||||
* e.g. [0, 'change', 'change-minified']
|
||||
* we want to set the reflected property with the privateName: dataValue[i+2]
|
||||
*/
|
||||
for (let i = 0; i < dataValue.length; i += 3) {
|
||||
setNgReflectProperty(lView, element, type, dataValue[i + 2] as string, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateProperty(
|
||||
hostView: LView, element: RElement | RComment, propName: string, tNode: TNode): boolean {
|
||||
// The property is considered valid if the element matches the schema, it exists on the element
|
||||
|
|
|
@ -1171,6 +1171,40 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
|||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR - value 1 - value 2 (fr)`);
|
||||
});
|
||||
|
||||
it('should create corresponding ng-reflect properties', () => {
|
||||
@Component({
|
||||
selector: 'welcome',
|
||||
template: '{{ messageText }}',
|
||||
})
|
||||
class WelcomeComp {
|
||||
@Input() messageText !: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<welcome
|
||||
messageText="Hello"
|
||||
i18n-messageText="Welcome message description">
|
||||
</welcome>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [App, WelcomeComp],
|
||||
});
|
||||
loadTranslations({
|
||||
[computeMsgId('Hello')]: 'Bonjour',
|
||||
});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
const comp = fixture.debugElement.query(By.css('welcome'));
|
||||
expect(comp.attributes['messagetext']).toBe('Bonjour');
|
||||
expect(comp.attributes['ng-reflect-message-text']).toBe('Bonjour');
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with directives and host bindings', () => {
|
||||
|
|
Loading…
Reference in New Issue