diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts
index 2a13f675b1..6a63a39bd4 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts
@@ -1270,6 +1270,28 @@ describe('i18n support in the view compiler', () => {
verify(input, output);
});
+
+ it('should generate event listeners instructions before i18n ones', () => {
+ const input = `
+
Hello
+ `;
+
+ const output = String.raw `
+ const $_c0$ = [${AttributeMarker.Bindings}, "click"];
+ const $MSG_EXTERNAL_3902961887793684628$$APP_SPEC_TS_1$ = goog.getMsg("Hello");
+ …
+ template: function MyComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ $r3$.ɵelementStart(0, "div", $_c0$);
+ $r3$.ɵlistener("click", function MyComponent_Template_div_click_0_listener($event) { return ctx.onClick(); });
+ $r3$.ɵi18n(1, $MSG_EXTERNAL_3902961887793684628$$APP_SPEC_TS_1$);
+ $r3$.ɵelementEnd();
+ }
+ }
+ `;
+
+ verify(input, output);
+ });
});
describe('self-closing i18n instructions', () => {
diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts
index 5038bf8b44..bca6947265 100644
--- a/packages/compiler/src/render3/view/template.ts
+++ b/packages/compiler/src/render3/view/template.ts
@@ -657,12 +657,6 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
}
}
- // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes ones,
- // to make sure i18nAttributes instruction targets current element at runtime.
- if (isI18nRootElement) {
- this.i18nStart(element.sourceSpan, element.i18n !, createSelfClosingI18nInstruction);
- }
-
// The style bindings code is placed into two distinct blocks within the template function AOT
// code: creation and update. The creation code contains the `elementStyling` instructions
// which will apply the collected binding values to the element. `elementStyling` is
@@ -680,6 +674,12 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
outputAst.sourceSpan, R3.listener,
this.prepareListenerParameter(element.name, outputAst, elementIndex));
});
+
+ // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
+ // listeners, to make sure i18nAttributes instruction targets current element at runtime.
+ if (isI18nRootElement) {
+ this.i18nStart(element.sourceSpan, element.i18n !, createSelfClosingI18nInstruction);
+ }
}
// the code here will collect all update-level styling instructions and add them to the
diff --git a/packages/core/test/i18n_integration_spec.ts b/packages/core/test/i18n_integration_spec.ts
index 7a9e297d39..e04bc596e0 100644
--- a/packages/core/test/i18n_integration_spec.ts
+++ b/packages/core/test/i18n_integration_spec.ts
@@ -28,6 +28,9 @@ class MyComp {
age = 20;
count = 2;
otherLabel = 'other label';
+ clicks = 0;
+
+ onClick() { this.clicks++; }
}
const TRANSLATIONS: any = {
@@ -254,6 +257,23 @@ onlyInIvy('Ivy i18n logic').describe('i18n', function() {
const element = fixture.nativeElement.firstChild;
expect(element).toHaveText('Bonjour John');
});
+
+ it('should work correctly with event listeners', () => {
+ const content = 'Hello {{ name }}';
+ const template = `
+ ${content}
+ `;
+ const fixture = getFixtureWithOverrides({template});
+
+ const element = fixture.nativeElement.firstChild;
+ const instance = fixture.componentInstance;
+
+ expect(element).toHaveText('Bonjour John');
+ expect(instance.clicks).toBe(0);
+
+ element.click();
+ expect(instance.clicks).toBe(1);
+ });
});
describe('ng-container and ng-template support', () => {