Skip to content

Commit 8be2c48

Browse files
crisbetodylhunn
authored andcommitted
feat(core): implement new block syntax (angular#51891)
Switches the syntax for blocks from `{#block}{/block}` to `@block {}` based on the feedback from the community. Read more about the decision-making process in our blog: https://blog.angular.io/meet-angulars-new-control-flow-a02c6eee7843 The existing block types changed in the following ways: **Conditional blocks:** ```html <!-- Before --> {#if cond} Main content {:else if otherCond} Else if content {:else} Else content {/if} <!-- After --> @if (cond) { Main content } @else if (otherCond) { Else if content } @else { Else content } ``` **Deferred blocks** ```html <!-- Before --> {#defer when isLoaded} Main content {:loading} Loading... {:placeholder} <icon>pending</icon> {:error} Failed to load {/defer} <!-- After --> @defer (when isLoaded) { Main content } @Loading { Loading... } @Placeholder { <icon>pending</icon> } @error { Failed to load } ``` **Switch blocks:** ```html <!-- Before --> {#switch value} {:case 1} One {:case 2} Two {:default} Default {/switch} <!-- After --> @switch (value) { @case (1) { One } @case (2) { Two } @default { Default } } ``` **For loops** ```html <!-- Before --> {#for item of items; track item} {{item.name}} {:empty} No items {/for} <!-- After --> @for (item of items; track item) { {{item.name}} } @empty { No items } ``` PR Close angular#51891
1 parent 5f6cc7c commit 8be2c48

File tree

102 files changed

+2508
-2078
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+2508
-2078
lines changed

packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ export class ComponentDecoratorHandler implements
689689
}
690690

691691
// Register all Directives and Pipes used at the top level (outside
692-
// of any `{#defer}` blocks), which would be eagerly referenced.
692+
// of any defer blocks), which would be eagerly referenced.
693693
const eagerlyUsed = new Set<ClassDeclaration>();
694694
for (const dir of bound.getEagerlyUsedDirectives()) {
695695
eagerlyUsed.add(dir.ref.node);
@@ -702,7 +702,7 @@ export class ComponentDecoratorHandler implements
702702
}
703703

704704
// Set of Directives and Pipes used across the entire template,
705-
// including all `{#defer}` blocks.
705+
// including all defer blocks.
706706
const wholeTemplateUsed = new Set<ClassDeclaration>(eagerlyUsed);
707707
for (const bound of deferBlocks.values()) {
708708
for (const dir of bound.getEagerlyUsedDirectives()) {
@@ -790,7 +790,7 @@ export class ComponentDecoratorHandler implements
790790
decl => decl.kind === R3TemplateDependencyKind.NgModule ||
791791
eagerlyUsed.has(decl.ref.node));
792792

793-
// Process information related to `{#defer}` blocks
793+
// Process information related to defer blocks
794794
this.resolveDeferBlocks(deferBlocks, declarations, data, analysis, eagerlyUsed, bound);
795795

796796
const cyclesFromDirectives = new Map<UsedDirective, Cycle>();
@@ -1213,7 +1213,7 @@ export class ComponentDecoratorHandler implements
12131213

12141214
if (eagerlyUsedDecls.has(decl.node)) {
12151215
// Can't defer-load symbols that are eagerly referenced as a dependency
1216-
// in a template outside of a `{#defer}` block.
1216+
// in a template outside of a defer block.
12171217
continue;
12181218
}
12191219

packages/compiler-cli/src/ngtsc/core/api/src/options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export interface TestOnlyOptions {
3232

3333
/**
3434
* Names of the blocks that should be enabled. E.g. `_enabledBlockTypes: ['defer']`
35-
* would allow usages of `{#defer}{/defer}` in templates.
35+
* would allow usages of `@defer {}` in templates.
3636
*
3737
* @internal
3838
*/

packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ export enum ErrorCode {
266266
*
267267
* ```
268268
* <ng-template let-ref>
269-
* {#for item of items; track ref}{/for}
269+
* @for (item of items; track ref) {}
270270
* </ng-template>
271271
* ```
272272
*/

packages/compiler-cli/src/ngtsc/imports/src/deferred_symbol_tracker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type AssumeEager = typeof AssumeEager;
1818
*
1919
* This information is later used to determine whether it's safe to drop
2020
* a regular import of this symbol (actually the entire import declaration)
21-
* in favor of using a dynamic import for cases when `{#defer}` blocks are used.
21+
* in favor of using a dynamic import for cases when defer blocks are used.
2222
*/
2323
export class DeferredSymbolTracker {
2424
private readonly imports =
@@ -92,7 +92,7 @@ export class DeferredSymbolTracker {
9292
const identifiers = symbolMap.get(identifier.text) as Set<ts.Identifier>;
9393

9494
// Drop the current identifier, since we are trying to make it deferrable
95-
// (it's used as a dependency in one of the `{#defer}` blocks).
95+
// (it's used as a dependency in one of the defer blocks).
9696
identifiers.delete(identifier);
9797
}
9898

packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,7 @@ class TcbComponentContextCompletionOp extends TcbOp {
11521152
}
11531153

11541154
/**
1155-
* A `TcbOp` which renders a variable defined inside of block syntax (e.g. `{#if expr; as var}`).
1155+
* A `TcbOp` which renders a variable defined inside of block syntax (e.g. `@if (expr; as var) {}`).
11561156
*
11571157
* Executing this operation returns the identifier which can be used to refer to the variable.
11581158
*/
@@ -1177,7 +1177,7 @@ class TcbBlockVariableOp extends TcbOp {
11771177

11781178
/**
11791179
* A `TcbOp` which renders a variable that is implicitly available within a block (e.g. `$count`
1180-
* in a `{#for}` block).
1180+
* in a `@for` block).
11811181
*
11821182
* Executing this operation returns the identifier which can be used to refer to the variable.
11831183
*/

packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,12 +1365,15 @@ describe('type check blocks', () => {
13651365

13661366
it('should generate bindings inside deferred blocks', () => {
13671367
const TEMPLATE = `
1368-
{#defer}
1368+
@defer {
13691369
{{main()}}
1370-
{:placeholder}{{placeholder()}}
1371-
{:loading}{{loading()}}
1372-
{:error}{{error()}}
1373-
{/defer}
1370+
} @placeholder {
1371+
{{placeholder()}}
1372+
} @loading {
1373+
{{loading()}}
1374+
} @error {
1375+
{{error()}}
1376+
}
13741377
`;
13751378

13761379
expect(deferredTcb(TEMPLATE))
@@ -1380,15 +1383,19 @@ describe('type check blocks', () => {
13801383

13811384
it('should generate `when` trigger', () => {
13821385
const TEMPLATE = `
1383-
{#defer when shouldShow() && isVisible}{{main()}}{/defer}
1386+
@defer (when shouldShow() && isVisible) {
1387+
{{main()}}
1388+
}
13841389
`;
13851390

13861391
expect(deferredTcb(TEMPLATE)).toContain('((this).shouldShow()) && (((this).isVisible));');
13871392
});
13881393

13891394
it('should generate `prefetch when` trigger', () => {
13901395
const TEMPLATE = `
1391-
{#defer prefetch when shouldShow() && isVisible}{{main()}}{/defer}
1396+
@defer (prefetch when shouldShow() && isVisible) {
1397+
{{main()}}
1398+
}
13921399
`;
13931400

13941401
expect(deferredTcb(TEMPLATE)).toContain('((this).shouldShow()) && (((this).isVisible));');
@@ -1405,12 +1412,15 @@ describe('type check blocks', () => {
14051412

14061413
it('should generate an if block', () => {
14071414
const TEMPLATE = `
1408-
{#if expr === 0}
1415+
@if (expr === 0) {
14091416
{{main()}}
1410-
{:else if expr1 === 1}{{one()}}
1411-
{:else if expr2 === 2}{{two()}}
1412-
{:else}{{other()}}
1413-
{/if}
1417+
} @else if (expr1 === 1) {
1418+
{{one()}}
1419+
} @else if (expr2 === 2) {
1420+
{{two()}}
1421+
} @else {
1422+
{{other()}}
1423+
}
14141424
`;
14151425

14161426
expect(conditionalTcb(TEMPLATE))
@@ -1422,7 +1432,9 @@ describe('type check blocks', () => {
14221432
});
14231433

14241434
it('should generate an if block with an `as` expression', () => {
1425-
const TEMPLATE = `{#if expr === 1; as alias}{{alias}}{/if}`;
1435+
const TEMPLATE = `@if (expr === 1; as alias) {
1436+
{{alias}}
1437+
}`;
14261438

14271439
expect(conditionalTcb(TEMPLATE))
14281440
.toContain(
@@ -1431,11 +1443,17 @@ describe('type check blocks', () => {
14311443

14321444
it('should generate a switch block', () => {
14331445
const TEMPLATE = `
1434-
{#switch expr}
1435-
{:case 1}{{one()}}
1436-
{:case 2}{{two()}}
1437-
{:default}{{default()}}
1438-
{/switch}
1446+
@switch (expr) {
1447+
@case (1) {
1448+
{{one()}}
1449+
}
1450+
@case (2) {
1451+
{{two()}}
1452+
}
1453+
@default {
1454+
{{default()}}
1455+
}
1456+
}
14391457
`;
14401458

14411459
expect(conditionalTcb(TEMPLATE))
@@ -1447,11 +1465,17 @@ describe('type check blocks', () => {
14471465
it('should generate a switch block inside a template', () => {
14481466
const TEMPLATE = `
14491467
<ng-template let-expr="exp">
1450-
{#switch expr()}
1451-
{:case 'one'}{{one()}}
1452-
{:case 'two'}{{two()}}
1453-
{:default}{{default()}}
1454-
{/switch}
1468+
@switch (expr()) {
1469+
@case ('one') {
1470+
{{one()}}
1471+
}
1472+
@case ('two') {
1473+
{{two()}}
1474+
}
1475+
@default {
1476+
{{default()}}
1477+
}
1478+
}
14551479
</ng-template>
14561480
`;
14571481

@@ -1463,7 +1487,6 @@ describe('type check blocks', () => {
14631487
});
14641488
});
14651489

1466-
// TODO(crisbeto): tests for the tracking function.
14671490
describe('for loop blocks', () => {
14681491
// TODO(crisbeto): temporary utility while for loop blocks are disabled by default
14691492
function loopTcb(template: string): string {
@@ -1472,10 +1495,11 @@ describe('type check blocks', () => {
14721495

14731496
it('should generate a for block', () => {
14741497
const TEMPLATE = `
1475-
{#for item of items; track item}
1498+
@for (item of items; track item) {
14761499
{{main(item)}}
1477-
{:empty}{{empty()}}
1478-
{/for}
1500+
} @empty {
1501+
{{empty()}}
1502+
}
14791503
`;
14801504

14811505
const result = loopTcb(TEMPLATE);
@@ -1486,9 +1510,9 @@ describe('type check blocks', () => {
14861510

14871511
it('should generate a for block with implicit variables', () => {
14881512
const TEMPLATE = `
1489-
{#for item of items; track item}
1513+
@for (item of items; track item) {
14901514
{{$index}} {{$first}} {{$last}} {{$even}} {{$odd}} {{$count}}
1491-
{/for}
1515+
}
14921516
`;
14931517

14941518
const result = loopTcb(TEMPLATE);
@@ -1504,9 +1528,9 @@ describe('type check blocks', () => {
15041528

15051529
it('should generate a for block with aliased variables', () => {
15061530
const TEMPLATE = `
1507-
{#for item of items; track item; let i = $index, f = $first, l = $last, e = $even, o = $odd, c = $count}
1531+
@for (item of items; track item; let i = $index, f = $first, l = $last, e = $even, o = $odd, c = $count) {
15081532
{{i}} {{f}} {{l}} {{e}} {{o}} {{c}}
1509-
{/for}
1533+
}
15101534
`;
15111535

15121536
const result = loopTcb(TEMPLATE);
@@ -1522,7 +1546,7 @@ describe('type check blocks', () => {
15221546

15231547
it('should read an implicit variable from the component scope if it is aliased', () => {
15241548
const TEMPLATE = `
1525-
{#for item of items; track item; let i = $index} {{$index}} {{i}} {/for}
1549+
@for (item of items; track item; let i = $index) { {{$index}} {{i}} }
15261550
`;
15271551

15281552
const result = loopTcb(TEMPLATE);
@@ -1533,13 +1557,13 @@ describe('type check blocks', () => {
15331557

15341558
it('should read variable from a parent for loop', () => {
15351559
const TEMPLATE = `
1536-
{#for item of items; track item; let indexAlias = $index}
1560+
@for (item of items; track item; let indexAlias = $index) {
15371561
{{item}} {{indexAlias}}
15381562
1539-
{#for inner of item.items; track inner}
1563+
@for (inner of item.items; track inner) {
15401564
{{item}} {{indexAlias}} {{inner}} {{$index}}
1541-
{/for}
1542-
{/for}
1565+
}
1566+
}
15431567
`;
15441568

15451569
const result = loopTcb(TEMPLATE);
@@ -1552,7 +1576,7 @@ describe('type check blocks', () => {
15521576
});
15531577

15541578
it('should generate the tracking expression of a for loop', () => {
1555-
const result = loopTcb(`{#for item of items; track trackingFn($index, item, prop)}{/for}`);
1579+
const result = loopTcb(`@for (item of items; track trackingFn($index, item, prop)) {}`);
15561580

15571581
expect(result).toContain(
15581582
'for (const item of ((this).items)) { var _t1: number = null!; var _t2 = item;');

0 commit comments

Comments
 (0)