mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(compiler): generate switch block instructions (#51380)
Adds the logic to generate the instructions for `switch` instructions. For the following block:
```html
{#switch value()}
{:case 0} case 0
{:case 1} case 1
{:case 2} case 2
{:default} default
{/switch}
```
The compiler will produce the following output:
```ts
function App_Template(rf, ctx) {
if (rf & 1) {
ɵɵtemplate(0, App_Case_0_Template, 1, 0);
ɵɵtemplate(1, App_Case_1_Template, 1, 0);
ɵɵtemplate(2, App_Case_2_Template, 1, 0);
ɵɵtemplate(3, App_Case_3_Template, 1, 0);
}
if (rf & 2) {
let App_contFlowTmp;
ɵɵconditional(0, (App_contFlowTmp = ctx.value()) === 0 ? 0 : App_contFlowTmp === 1 ? 1 : App_contFlowTmp === 2 ? 2 : 3);
}
}
```
PR Close #51380
This commit is contained in:
parent
5bd9fbd2c3
commit
0c4c773fca
13 changed files with 653 additions and 2 deletions
|
|
@ -0,0 +1,230 @@
|
|||
/****************************************************************************************************
|
||||
* PARTIAL FILE: basic_switch.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.message = 'hello';
|
||||
}
|
||||
value() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:case 2} case 2
|
||||
{:default} default
|
||||
{/switch}
|
||||
</div>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:case 2} case 2
|
||||
{:default} default
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: basic_switch.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
message: string;
|
||||
value(): number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, false, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: switch_without_default.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.message = 'hello';
|
||||
this.value = () => 1;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:case 2} case 2
|
||||
{/switch}
|
||||
</div>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:case 2} case 2
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: switch_without_default.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
message: string;
|
||||
value: () => number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, false, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: nested_switch.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.message = 'hello';
|
||||
this.value = () => 1;
|
||||
this.nestedValue = () => 2;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1}
|
||||
{#switch nestedValue()}
|
||||
{:case 0} nested case 0
|
||||
{:case 1} nested case 1
|
||||
{:case 2} nested case 2
|
||||
{/switch}
|
||||
{:case 2} case 2
|
||||
{/switch}
|
||||
</div>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1}
|
||||
{#switch nestedValue()}
|
||||
{:case 0} nested case 0
|
||||
{:case 1} nested case 1
|
||||
{:case 2} nested case 2
|
||||
{/switch}
|
||||
{:case 2} case 2
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: nested_switch.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
message: string;
|
||||
value: () => number;
|
||||
nestedValue: () => number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, false, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: switch_with_pipe.js
|
||||
****************************************************************************************************/
|
||||
import { Component, Pipe } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class TestPipe {
|
||||
tranform(value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
TestPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
||||
TestPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestPipe, isStandalone: true, name: "test" });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestPipe, decorators: [{
|
||||
type: Pipe,
|
||||
args: [{ standalone: true, name: 'test' }]
|
||||
}] });
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.message = 'hello';
|
||||
this.value = () => 1;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value() | test}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:default} default
|
||||
{/switch}
|
||||
</div>
|
||||
`, isInline: true, dependencies: [{ kind: "pipe", type: TestPipe, name: "test" }] });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value() | test}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:default} default
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
standalone: true,
|
||||
imports: [TestPipe]
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: switch_with_pipe.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class TestPipe {
|
||||
tranform(value: unknown): unknown;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TestPipe, never>;
|
||||
static ɵpipe: i0.ɵɵPipeDeclaration<TestPipe, "test", true>;
|
||||
}
|
||||
export declare class MyApp {
|
||||
message: string;
|
||||
value: () => number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"$schema": "../test_case_schema.json",
|
||||
"cases": [
|
||||
{
|
||||
"description": "should generate a basic switch block",
|
||||
"angularCompilerOptions": {
|
||||
"_enabledBlockTypes": ["switch"]
|
||||
},
|
||||
"inputFiles": [
|
||||
"basic_switch.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "basic_switch_template.js",
|
||||
"generated": "basic_switch.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
],
|
||||
"skipForTemplatePipeline": true
|
||||
},
|
||||
{
|
||||
"description": "should generate a switch block without a default block",
|
||||
"angularCompilerOptions": {
|
||||
"_enabledBlockTypes": ["switch"]
|
||||
},
|
||||
"inputFiles": [
|
||||
"switch_without_default.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "switch_without_default_template.js",
|
||||
"generated": "switch_without_default.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
],
|
||||
"skipForTemplatePipeline": true
|
||||
},
|
||||
{
|
||||
"description": "should generate nested switch blocks",
|
||||
"angularCompilerOptions": {
|
||||
"_enabledBlockTypes": ["switch"]
|
||||
},
|
||||
"inputFiles": [
|
||||
"nested_switch.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "nested_switch_template.js",
|
||||
"generated": "nested_switch.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
],
|
||||
"skipForTemplatePipeline": true
|
||||
},
|
||||
{
|
||||
"description": "should generate switch block with a pipe in its expression",
|
||||
"angularCompilerOptions": {
|
||||
"_enabledBlockTypes": ["switch"]
|
||||
},
|
||||
"inputFiles": [
|
||||
"switch_with_pipe.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "switch_with_pipe_template.js",
|
||||
"generated": "switch_with_pipe.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
],
|
||||
"skipForTemplatePipeline": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:case 2} case 2
|
||||
{:default} default
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class MyApp {
|
||||
message = 'hello';
|
||||
|
||||
value() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
function MyApp_Case_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 0 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_3_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 1 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_4_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 2 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_5_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " default ");
|
||||
}
|
||||
}
|
||||
…
|
||||
function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵelementStart(0, "div");
|
||||
$r3$.ɵɵtext(1);
|
||||
$r3$.ɵɵtemplate(2, MyApp_Case_2_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(3, MyApp_Case_3_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(4, MyApp_Case_4_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(5, MyApp_Case_5_Template, 1, 0);
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
let MyApp_contFlowTmp;
|
||||
$r3$.ɵɵadvance(1);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.message, " ");
|
||||
$r3$.ɵɵadvance(1);
|
||||
$r3$.ɵɵconditional(2, (MyApp_contFlowTmp = ctx.value()) === 0 ? 2 : MyApp_contFlowTmp === 1 ? 3 : MyApp_contFlowTmp === 2 ? 4 : 5);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1}
|
||||
{#switch nestedValue()}
|
||||
{:case 0} nested case 0
|
||||
{:case 1} nested case 1
|
||||
{:case 2} nested case 2
|
||||
{/switch}
|
||||
{:case 2} case 2
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class MyApp {
|
||||
message = 'hello';
|
||||
value = () => 1;
|
||||
nestedValue = () => 2;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
function MyApp_Case_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 0 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_3_Case_0_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " nested case 0 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_3_Case_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " nested case 1 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_3_Case_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " nested case 2 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_3_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtemplate(0, MyApp_Case_3_Case_0_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(1, MyApp_Case_3_Case_1_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(2, MyApp_Case_3_Case_2_Template, 1, 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const ctx_r1 = $r3$.ɵɵnextContext();
|
||||
let MyApp_Case_3_contFlowTmp;
|
||||
$r3$.ɵɵconditional(0, (MyApp_Case_3_contFlowTmp = ctx_r1.nestedValue()) === 0 ? 0 : MyApp_Case_3_contFlowTmp === 1 ? 1 : MyApp_Case_3_contFlowTmp === 2 ? 2 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_4_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 2 ");
|
||||
}
|
||||
}
|
||||
…
|
||||
function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵelementStart(0, "div");
|
||||
$r3$.ɵɵtext(1);
|
||||
$r3$.ɵɵtemplate(2, MyApp_Case_2_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(3, MyApp_Case_3_Template, 3, 4);
|
||||
$r3$.ɵɵtemplate(4, MyApp_Case_4_Template, 1, 0);
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
let MyApp_contFlowTmp;
|
||||
$r3$.ɵɵadvance(1);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.message, " ");
|
||||
$r3$.ɵɵadvance(1);
|
||||
$r3$.ɵɵconditional(2, (MyApp_contFlowTmp = ctx.value()) === 0 ? 2 : MyApp_contFlowTmp === 1 ? 3 : MyApp_contFlowTmp === 2 ? 4 : -1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import {Component, Pipe} from '@angular/core';
|
||||
|
||||
@Pipe({standalone: true, name: 'test'})
|
||||
export class TestPipe {
|
||||
tranform(value: unknown) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value() | test}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:default} default
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
standalone: true,
|
||||
imports: [TestPipe]
|
||||
})
|
||||
export class MyApp {
|
||||
message = 'hello';
|
||||
value = () => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵelementStart(0, "div");
|
||||
$r3$.ɵɵtext(1);
|
||||
$r3$.ɵɵpipe(2, "test");
|
||||
$r3$.ɵɵtemplate(3, MyApp_Case_3_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(4, MyApp_Case_4_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(5, MyApp_Case_5_Template, 1, 0);
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
let MyApp_contFlowTmp;
|
||||
$r3$.ɵɵadvance(1);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.message, " ");
|
||||
$r3$.ɵɵadvance(2);
|
||||
$r3$.ɵɵconditional(3, (MyApp_contFlowTmp = $r3$.ɵɵpipeBind1(2, 4, ctx.value())) === 0 ? 3 : MyApp_contFlowTmp === 1 ? 4 : 5);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div>
|
||||
{{message}}
|
||||
{#switch value()}
|
||||
{:case 0} case 0
|
||||
{:case 1} case 1
|
||||
{:case 2} case 2
|
||||
{/switch}
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class MyApp {
|
||||
message = 'hello';
|
||||
value = () => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
function MyApp_Case_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 0 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_3_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 1 ");
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp_Case_4_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0, " case 2 ");
|
||||
}
|
||||
}
|
||||
…
|
||||
function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵelementStart(0, "div");
|
||||
$r3$.ɵɵtext(1);
|
||||
$r3$.ɵɵtemplate(2, MyApp_Case_2_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(3, MyApp_Case_3_Template, 1, 0);
|
||||
$r3$.ɵɵtemplate(4, MyApp_Case_4_Template, 1, 0);
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
let MyApp_contFlowTmp;
|
||||
$r3$.ɵɵadvance(1);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.message, " ");
|
||||
$r3$.ɵɵadvance(1);
|
||||
$r3$.ɵɵconditional(2, (MyApp_contFlowTmp = ctx.value()) === 0 ? 2 : MyApp_contFlowTmp === 1 ? 3 : MyApp_contFlowTmp === 2 ? 4 : -1);
|
||||
}
|
||||
}
|
||||
|
|
@ -161,6 +161,8 @@ export class Identifiers {
|
|||
static deferPrefetchOnViewport:
|
||||
o.ExternalReference = {name: 'ɵɵdeferPrefetchOnViewport', moduleName: CORE};
|
||||
|
||||
static conditional: o.ExternalReference = {name: 'ɵɵconditional', moduleName: CORE};
|
||||
|
||||
static text: o.ExternalReference = {name: 'ɵɵtext', moduleName: CORE};
|
||||
|
||||
static enableBindings: o.ExternalReference = {name: 'ɵɵenableBindings', moduleName: CORE};
|
||||
|
|
|
|||
|
|
@ -178,6 +178,13 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
|
||||
/** Temporary variable declarations generated from visiting pipes, literals, etc. */
|
||||
private _tempVariables: o.Statement[] = [];
|
||||
|
||||
/**
|
||||
* Temporary variable used to store state between control flow instructions.
|
||||
* Should be accessed via the `allocateControlFlowTempVariable` method.
|
||||
*/
|
||||
private _controlFlowTempVariable: o.ReadVarExpr|null = null;
|
||||
|
||||
/**
|
||||
* List of callbacks to build nested templates. Nested templates must not be visited until
|
||||
* after the parent template has finished visiting all of its nodes. This ensures that all
|
||||
|
|
@ -1001,6 +1008,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
readonly visitDeferredBlockError = invalid;
|
||||
readonly visitDeferredBlockLoading = invalid;
|
||||
readonly visitDeferredBlockPlaceholder = invalid;
|
||||
readonly visitSwitchBlockCase = invalid;
|
||||
|
||||
visitBoundText(text: t.BoundText) {
|
||||
if (this.i18n) {
|
||||
|
|
@ -1088,6 +1096,66 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
return null;
|
||||
}
|
||||
|
||||
visitSwitchBlock(block: t.SwitchBlock): void {
|
||||
// Allocate slots for the primary block expression.
|
||||
const blockExpression = block.expression.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(blockExpression);
|
||||
|
||||
// We have to process the block in two steps: once here and again in the update instruction
|
||||
// callback in order to generate the correct expressions when pipes or pure functions are used.
|
||||
const caseData = block.cases.map(currentCase => {
|
||||
const index = this.createEmbeddedTemplateFn(
|
||||
null, currentCase.children, '_Case', currentCase.sourceSpan);
|
||||
let expression: AST|null = null;
|
||||
|
||||
if (currentCase.expression !== null) {
|
||||
expression = currentCase.expression.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(expression);
|
||||
}
|
||||
|
||||
return {index, expression};
|
||||
});
|
||||
|
||||
// Use the index of the first block as the index for the entire container.
|
||||
const containerIndex = caseData[0].index;
|
||||
|
||||
this.updateInstructionWithAdvance(containerIndex, block.sourceSpan, R3.conditional, () => {
|
||||
const generateCases = (caseIndex: number): o.Expression => {
|
||||
// If we've gone beyond the last branch, return the special -1
|
||||
// value which means that no view will be rendered.
|
||||
if (caseIndex > caseData.length - 1) {
|
||||
return o.literal(-1);
|
||||
}
|
||||
|
||||
const {index, expression} = caseData[caseIndex];
|
||||
|
||||
// If the case has no expression, it means that it's the `default` case.
|
||||
// Return its index and stop the recursion. Assumes that there's only one
|
||||
// `default` condition and that it's defined last.
|
||||
if (expression === null) {
|
||||
return o.literal(index);
|
||||
}
|
||||
|
||||
// If this is the very first comparison, we need to assign the value of the primary
|
||||
// expression as a part of the comparison so the remaining cases can reuse it. In practice
|
||||
// this looks as follows:
|
||||
// ```
|
||||
// let temp;
|
||||
// conditional(1, (temp = ctx.foo) === 1 ? 1 : temp === 2 ? 2 : temp === 3 ? 3 : 4);
|
||||
// ```
|
||||
const comparisonTarget = caseIndex === 0 ?
|
||||
this.allocateControlFlowTempVariable().set(
|
||||
this.convertPropertyBinding(blockExpression)) :
|
||||
this.allocateControlFlowTempVariable();
|
||||
|
||||
return comparisonTarget.identical(this.convertPropertyBinding(expression))
|
||||
.conditional(o.literal(index), generateCases(caseIndex + 1));
|
||||
};
|
||||
|
||||
return [o.literal(containerIndex), generateCases(0)];
|
||||
});
|
||||
}
|
||||
|
||||
visitDeferredBlock(deferred: t.DeferredBlock): void {
|
||||
const {loading, placeholder, error, triggers, prefetchTriggers} = deferred;
|
||||
const primaryTemplateIndex =
|
||||
|
|
@ -1233,8 +1301,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
}
|
||||
|
||||
// TODO: implement control flow instructions
|
||||
visitSwitchBlock(block: t.SwitchBlock): void {}
|
||||
visitSwitchBlockCase(block: t.SwitchBlockCase): void {}
|
||||
visitForLoopBlock(block: t.ForLoopBlock): void {}
|
||||
visitForLoopBlockEmpty(block: t.ForLoopBlockEmpty): void {}
|
||||
visitIfBlock(block: t.IfBlock): void {}
|
||||
|
|
@ -1406,6 +1472,23 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a variable that can be used to
|
||||
* store the state between control flow instructions.
|
||||
*/
|
||||
private allocateControlFlowTempVariable(): o.ReadVarExpr {
|
||||
// Note: the assumption here is that we'll only need one temporary variable for all control
|
||||
// flow instructions. It's expected that any instructions will overwrite it before passing it
|
||||
// into the parameters.
|
||||
if (this._controlFlowTempVariable === null) {
|
||||
const name = `${this.contextName}_contFlowTmp`;
|
||||
this._tempVariables.push(new o.DeclareVarStmt(name));
|
||||
this._controlFlowTempVariable = o.variable(name);
|
||||
}
|
||||
|
||||
return this._controlFlowTempVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares all attribute expression values for the `TAttributes` array.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ export const angularCoreEnv: {[name: string]: Function} =
|
|||
'ɵɵclassProp': r3.ɵɵclassProp,
|
||||
'ɵɵadvance': r3.ɵɵadvance,
|
||||
'ɵɵtemplate': r3.ɵɵtemplate,
|
||||
'ɵɵconditional': r3.ɵɵconditional,
|
||||
'ɵɵdefer': r3.ɵɵdefer,
|
||||
'ɵɵdeferWhen': r3.ɵɵdeferWhen,
|
||||
'ɵɵdeferOnIdle': r3.ɵɵdeferOnIdle,
|
||||
|
|
|
|||
Loading…
Reference in a new issue