fix(ivy): template inputs/outputs should not be bound in template scope (#30669)
The R3TargetBinder "binds" an Angular template AST, computing semantic information regarding the template and making it accessible. One of the binding passes previously had a bug, where for the following template: <div *ngIf="foo as foo"></div> which desugars to: <ng-template ngIf [ngIf]="foo" let-foo="ngIf"> <div></div> </ng-template> would have the `[ngIf]` binding processed twice - in both the scope which contains the `<ng-template>` and the scope inside the template. The bug arises because during the latter, `foo` is a variable defined by `let-foo`, and so the R3TargetBinder would incorrectly learn that `foo` inside `[ngIf]` maps to that variable. This commit fixes the bug by only processing inputs, outputs, and templateAttrs from `Template`s in the outer scope. PR Close #30669
This commit is contained in:
parent
b4644d7bb0
commit
b61784948a
|
@ -270,6 +270,31 @@ describe('ngtsc type checking', () => {
|
||||||
expect(diags[0].messageText).toContain('does_not_exist');
|
expect(diags[0].messageText).toContain('does_not_exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should property type-check a microsyntax variable with the same name as the expression',
|
||||||
|
() => {
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {Component, Input, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test',
|
||||||
|
template: '<div *ngIf="foo as foo">{{foo}}</div>',
|
||||||
|
})
|
||||||
|
export class TestCmp<T extends {name: string}> {
|
||||||
|
foo: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [TestCmp],
|
||||||
|
imports: [CommonModule],
|
||||||
|
})
|
||||||
|
export class Module {}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const diags = env.driveDiagnostics();
|
||||||
|
expect(diags.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should properly type-check inherited directives', () => {
|
it('should properly type-check inherited directives', () => {
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
import {Component, Directive, Input, NgModule} from '@angular/core';
|
import {Component, Directive, Input, NgModule} from '@angular/core';
|
||||||
|
|
|
@ -372,12 +372,8 @@ class TemplateBinder extends RecursiveAstVisitor implements Visitor {
|
||||||
|
|
||||||
private ingest(template: Template|Node[]): void {
|
private ingest(template: Template|Node[]): void {
|
||||||
if (template instanceof Template) {
|
if (template instanceof Template) {
|
||||||
// For <ng-template>s, process inputs, outputs, template attributes,
|
// For <ng-template>s, process only variables and child nodes. Inputs, outputs, templateAttrs,
|
||||||
// variables, and child nodes.
|
// and references were all processed in the scope of the containing template.
|
||||||
// References were processed in the scope of the containing template.
|
|
||||||
template.inputs.forEach(this.visitNode);
|
|
||||||
template.outputs.forEach(this.visitNode);
|
|
||||||
template.templateAttrs.forEach(this.visitNode);
|
|
||||||
template.variables.forEach(this.visitNode);
|
template.variables.forEach(this.visitNode);
|
||||||
template.children.forEach(this.visitNode);
|
template.children.forEach(this.visitNode);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue