fix(ivy): fix bug with banana-in-a-box expressions in nested templates (#25321)
Inside of a nested template, an attempt to generate code for a banana- in-a-box expression would cause a crash in the _AstToIrVisitor, as it was not handling the case where a write would be generated to a local variable. This change supports such a mode of operation. PR Close #25321
This commit is contained in:
parent
02e201ab1a
commit
fefc860f35
|
@ -640,4 +640,21 @@ describe('ngtsc behavioral tests', () => {
|
||||||
expect(emptyFactory).toContain(`import * as i0 from '@angular/core';`);
|
expect(emptyFactory).toContain(`import * as i0 from '@angular/core';`);
|
||||||
expect(emptyFactory).toContain(`export var ɵNonEmptyModule = true;`);
|
expect(emptyFactory).toContain(`export var ɵNonEmptyModule = true;`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should compile a banana-in-a-box inside of a template', () => {
|
||||||
|
writeConfig();
|
||||||
|
write('test.ts', `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<div *tmpl [(bananaInABox)]="prop"></div>',
|
||||||
|
selector: 'test'
|
||||||
|
})
|
||||||
|
class TestCmp {}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const exitCode = main(['-p', basePath], errorSpy);
|
||||||
|
expect(errorSpy).not.toHaveBeenCalled();
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -449,14 +449,28 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
|
|
||||||
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||||
const receiver: o.Expression = this._visit(ast.receiver, _Mode.Expression);
|
const receiver: o.Expression = this._visit(ast.receiver, _Mode.Expression);
|
||||||
|
|
||||||
|
let varExpr: o.ReadPropExpr|null = null;
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
const varExpr = this._getLocal(ast.name);
|
const localExpr = this._getLocal(ast.name);
|
||||||
if (varExpr) {
|
if (localExpr) {
|
||||||
throw new Error('Cannot assign to a reference or variable!');
|
if (localExpr instanceof o.ReadPropExpr) {
|
||||||
|
// If the local variable is a property read expression, it's a reference
|
||||||
|
// to a 'context.property' value and will be used as the target of the
|
||||||
|
// write expression.
|
||||||
|
varExpr = localExpr;
|
||||||
|
} else {
|
||||||
|
// Otherwise it's an error.
|
||||||
|
throw new Error('Cannot assign to a reference or variable!');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return convertToStatementIfNeeded(
|
// If no local expression could be produced, use the original receiver's
|
||||||
mode, receiver.prop(ast.name).set(this._visit(ast.value, _Mode.Expression)));
|
// property as the target.
|
||||||
|
if (varExpr === null) {
|
||||||
|
varExpr = receiver.prop(ast.name);
|
||||||
|
}
|
||||||
|
return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
|
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
|
||||||
|
|
Loading…
Reference in New Issue