mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(compiler): type check deferred when and prefetch when triggers (#51570)
Adds type checking support to the deferred `when` and `prefetch when` triggers. PR Close #51570
This commit is contained in:
parent
0c8917b348
commit
75ab0bdf45
5 changed files with 77 additions and 13 deletions
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, BindingPipe, BindingType, BoundTarget, Call, DYNAMIC_TYPE, ImplicitReceiver, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SafeCall, SafePropertyRead, SchemaMetadata, ThisReceiver, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstDeferredBlock, TmplAstElement, TmplAstForLoopBlock, TmplAstIcu, TmplAstIfBlock, TmplAstNode, TmplAstReference, TmplAstSwitchBlock, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable, TransplantedType} from '@angular/compiler';
|
||||
import {AST, BindingPipe, BindingType, BoundTarget, Call, DYNAMIC_TYPE, ImplicitReceiver, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SafeCall, SafePropertyRead, SchemaMetadata, ThisReceiver, TmplAstBoundAttribute, TmplAstBoundDeferredTrigger, TmplAstBoundEvent, TmplAstBoundText, TmplAstDeferredBlock, TmplAstElement, TmplAstForLoopBlock, TmplAstIcu, TmplAstIfBlock, TmplAstNode, TmplAstReference, TmplAstSwitchBlock, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable, TransplantedType} from '@angular/compiler';
|
||||
import ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../imports';
|
||||
|
|
@ -407,12 +407,12 @@ class TcbTemplateBodyOp extends TcbOp {
|
|||
}
|
||||
|
||||
/**
|
||||
* A `TcbOp` which renders a text binding (interpolation) into the TCB.
|
||||
* A `TcbOp` which renders an Angular expression (e.g. `{{foo() && bar.baz}}`).
|
||||
*
|
||||
* Executing this operation returns nothing.
|
||||
*/
|
||||
class TcbTextInterpolationOp extends TcbOp {
|
||||
constructor(private tcb: Context, private scope: Scope, private binding: TmplAstBoundText) {
|
||||
class TcbExpressionOp extends TcbOp {
|
||||
constructor(private tcb: Context, private scope: Scope, private expression: AST) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
|
@ -421,7 +421,7 @@ class TcbTextInterpolationOp extends TcbOp {
|
|||
}
|
||||
|
||||
override execute(): null {
|
||||
const expr = tcbExpression(this.binding.value, this.tcb, this.scope);
|
||||
const expr = tcbExpression(this.expression, this.tcb, this.scope);
|
||||
this.scope.addStatement(ts.factory.createExpressionStatement(expr));
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1505,7 +1505,10 @@ class Scope {
|
|||
}
|
||||
this.checkAndAppendReferencesOfNode(node);
|
||||
} else if (node instanceof TmplAstDeferredBlock) {
|
||||
// TODO(crisbeto): type check `when` and `prefetchWhen` triggers.
|
||||
node.triggers.when !== undefined &&
|
||||
this.opQueue.push(new TcbExpressionOp(this.tcb, this, node.triggers.when.value));
|
||||
node.prefetchTriggers.when !== undefined &&
|
||||
this.opQueue.push(new TcbExpressionOp(this.tcb, this, node.prefetchTriggers.when.value));
|
||||
this.appendChildren(node);
|
||||
node.placeholder !== null && this.appendChildren(node.placeholder);
|
||||
node.loading !== null && this.appendChildren(node.loading);
|
||||
|
|
@ -1525,7 +1528,7 @@ class Scope {
|
|||
this.appendChildren(node);
|
||||
node.empty && this.appendChildren(node.empty);
|
||||
} else if (node instanceof TmplAstBoundText) {
|
||||
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node));
|
||||
this.opQueue.push(new TcbExpressionOp(this.tcb, this, node.value));
|
||||
} else if (node instanceof TmplAstIcu) {
|
||||
this.appendIcuExpressions(node);
|
||||
}
|
||||
|
|
@ -1684,11 +1687,11 @@ class Scope {
|
|||
|
||||
private appendIcuExpressions(node: TmplAstIcu): void {
|
||||
for (const variable of Object.values(node.vars)) {
|
||||
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable));
|
||||
this.opQueue.push(new TcbExpressionOp(this.tcb, this, variable.value));
|
||||
}
|
||||
for (const placeholder of Object.values(node.placeholders)) {
|
||||
if (placeholder instanceof TmplAstBoundText) {
|
||||
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder));
|
||||
this.opQueue.push(new TcbExpressionOp(this.tcb, this, placeholder.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1356,7 +1356,6 @@ describe('type check blocks', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// TODO(crisbeto): test for `when` and `prefetchWhen` triggers.
|
||||
describe('deferred blocks', () => {
|
||||
// TODO(crisbeto): temporary utility while deferred blocks are disabled by default
|
||||
function deferredTcb(template: string): string {
|
||||
|
|
@ -1378,6 +1377,22 @@ describe('type check blocks', () => {
|
|||
.toContain(
|
||||
'"" + ((this).main()); "" + ((this).placeholder()); "" + ((this).loading()); "" + ((this).error());');
|
||||
});
|
||||
|
||||
it('should generate `when` trigger', () => {
|
||||
const TEMPLATE = `
|
||||
{#defer when shouldShow() && isVisible}{{main()}}{/defer}
|
||||
`;
|
||||
|
||||
expect(deferredTcb(TEMPLATE)).toContain('((this).shouldShow()) && (((this).isVisible));');
|
||||
});
|
||||
|
||||
it('should generate `prefetch when` trigger', () => {
|
||||
const TEMPLATE = `
|
||||
{#defer prefetch when shouldShow() && isVisible}{{main()}}{/defer}
|
||||
`;
|
||||
|
||||
expect(deferredTcb(TEMPLATE)).toContain('((this).shouldShow()) && (((this).isVisible));');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(crisbeto): tests for the bindings of conditionals and context variables.
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ export declare class MyApp {
|
|||
import { Component, Pipe } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class TestPipe {
|
||||
tranform() {
|
||||
transform() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -499,7 +499,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
|
|||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class TestPipe {
|
||||
tranform(): boolean;
|
||||
transform(): boolean;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TestPipe, never>;
|
||||
static ɵpipe: i0.ɵɵPipeDeclaration<TestPipe, "testPipe", true>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import {Component, Pipe} from '@angular/core';
|
|||
|
||||
@Pipe({standalone: true, name: 'testPipe'})
|
||||
export class TestPipe {
|
||||
tranform() {
|
||||
transform() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3655,6 +3655,52 @@ suppress
|
|||
`Property 'does_not_exist_error' does not exist on type 'Main'.`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should check `when` trigger expression', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: \`
|
||||
{#defer when isVisible() || does_not_exist}Hello{/defer}
|
||||
\`,
|
||||
standalone: true,
|
||||
})
|
||||
export class Main {
|
||||
isVisible() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.map(d => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([
|
||||
`Property 'does_not_exist' does not exist on type 'Main'.`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should check `prefetch when` trigger expression', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: \`
|
||||
{#defer prefetch when isVisible() || does_not_exist}Hello{/defer}
|
||||
\`,
|
||||
standalone: true,
|
||||
})
|
||||
export class Main {
|
||||
isVisible() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.map(d => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([
|
||||
`Property 'does_not_exist' does not exist on type 'Main'.`,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('conditional blocks', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue