fix(compiler): incorrect context object being referenced from listener instructions inside embedded views (#42755)

Currently unless a listener inside of an embedded view tries to reference something from the parent view, or if the reference is a local ref, we don't generate the view restoration instructions and we allow for the value to be picked up from the context object in the function parameters. The problem is that the listener is only run during creation mode and the context object may have been swapped out afterwards.

These changes fix the issue by always generating the view restoration instructions for listeners inside templates.

Fixes #42698.

PR Close #42755
This commit is contained in:
Kristiyan Kostadinov
2021-07-03 11:59:58 +02:00
committed by Andrew Kushnir
parent 4c482bf3f1
commit 404c8d0d88
7 changed files with 148 additions and 18 deletions
@@ -636,3 +636,51 @@ export declare class MyModule {
static ɵinj: i0.ɵɵInjectorDeclaration<MyModule>;
}
/****************************************************************************************************
* PARTIAL FILE: embedded_view_listener_context.js
****************************************************************************************************/
import { Component, NgModule } from '@angular/core';
import * as i0 from "@angular/core";
export class MyComponent {
}
MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
<ng-template let-obj>
<button (click)="obj.value = 1">Change</button>
</ng-template>
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{
type: Component,
args: [{
selector: 'my-component',
template: `
<ng-template let-obj>
<button (click)="obj.value = 1">Change</button>
</ng-template>
`
}]
}] });
export class MyModule {
}
MyModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] });
MyModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, decorators: [{
type: NgModule,
args: [{ declarations: [MyComponent] }]
}] });
/****************************************************************************************************
* PARTIAL FILE: embedded_view_listener_context.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare class MyComponent {
static ɵfac: i0.ɵɵFactoryDeclaration<MyComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<MyComponent, "my-component", never, {}, {}, never, never>;
}
export declare class MyModule {
static ɵfac: i0.ɵɵFactoryDeclaration<MyModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<MyModule, [typeof MyComponent], never, never>;
static ɵinj: i0.ɵɵInjectorDeclaration<MyModule>;
}
@@ -281,6 +281,23 @@
"failureMessage": "Incorrect template"
}
]
},
{
"description": "should reference correct context in listener inside embedded view",
"inputFiles": [
"embedded_view_listener_context.ts"
],
"expectations": [
{
"files": [
{
"expected": "embedded_view_listener_context_template.js",
"generated": "embedded_view_listener_context.js"
}
],
"failureMessage": "Incorrect template"
}
]
}
]
}
@@ -0,0 +1,16 @@
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: `
<ng-template let-obj>
<button (click)="obj.value = 1">Change</button>
</ng-template>
`
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {
}
@@ -0,0 +1,13 @@
function MyComponent_ng_template_0_Template(rf, $ctx$) {
if (rf & 1) {
const _r3 = $i0$.ɵɵgetCurrentView();
$i0$.ɵɵelementStart(0, "button", 0);
$i0$.ɵɵlistener("click", function MyComponent_ng_template_0_Template_button_click_0_listener() {
const restoredCtx = $i0$.ɵɵrestoreView(_r3);
const $obj_r1$ = restoredCtx.$implicit;
return $obj_r1$.value = 1;
});
$i0$.ɵɵtext(1, "Change");
$i0$.ɵɵelementEnd();
}
}