diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js index 5d7d2b226ad..49da61b2371 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js @@ -1291,3 +1291,128 @@ export declare class TestCmp { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: deferred_on_viewport_with_options.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyApp { + constructor() { + this.message = 'hello'; + } +} +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: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` + {{message}} + @defer (on viewport({trigger: button, rootMargin: '123px', threshold: 59})) { + {{message}} + } @placeholder { + + } + `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{ + type: Component, + args: [{ + template: ` + {{message}} + @defer (on viewport({trigger: button, rootMargin: '123px', threshold: 59})) { + {{message}} + } @placeholder { + + } + `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: deferred_on_viewport_with_options.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyApp { + message: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: deferred_prefetch_on_viewport_with_options.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyApp { + constructor() { + this.message = 'hello'; + } +} +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: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` + {{message}} + @defer (prefetch on viewport({trigger: button, rootMargin: '123px', threshold: 59})) { + {{message}} + } @placeholder { + + } + `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{ + type: Component, + args: [{ + template: ` + {{message}} + @defer (prefetch on viewport({trigger: button, rootMargin: '123px', threshold: 59})) { + {{message}} + } @placeholder { + + } + `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: deferred_prefetch_on_viewport_with_options.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyApp { + message: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: deferred_hydrate_on_viewport_with_options.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyApp { + constructor() { + this.message = 'hello'; + } +} +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: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` + {{message}} + @defer (hydrate on viewport({rootMargin: '123px', threshold: 59})) { + {{message}} + } + `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{ + type: Component, + args: [{ + template: ` + {{message}} + @defer (hydrate on viewport({rootMargin: '123px', threshold: 59})) { + {{message}} + } + `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: deferred_hydrate_on_viewport_with_options.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyApp { + message: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json index 7ccfec6c228..7c64e7f3e8e 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json @@ -329,6 +329,51 @@ "failureMessage": "Defer block output with import alias does not match deferred_import_alias.js (possible alias resolution regression)" } ] + }, + { + "description": "should generate a defer block with a `on viewport` trigger that has options", + "inputFiles": ["deferred_on_viewport_with_options.ts"], + "expectations": [ + { + "files": [ + { + "expected": "deferred_on_viewport_with_options_template.js", + "generated": "deferred_on_viewport_with_options.js" + } + ], + "failureMessage": "Incorrect template" + } + ] + }, + { + "description": "should generate a defer block with a `prefetch on viewport` trigger that has options", + "inputFiles": ["deferred_prefetch_on_viewport_with_options.ts"], + "expectations": [ + { + "files": [ + { + "expected": "deferred_prefetch_on_viewport_with_options_template.js", + "generated": "deferred_prefetch_on_viewport_with_options.js" + } + ], + "failureMessage": "Incorrect template" + } + ] + }, + { + "description": "should generate a defer block with a `hydrate on viewport` trigger that has options", + "inputFiles": ["deferred_hydrate_on_viewport_with_options.ts"], + "expectations": [ + { + "files": [ + { + "expected": "deferred_hydrate_on_viewport_with_options_template.js", + "generated": "deferred_hydrate_on_viewport_with_options.js" + } + ], + "failureMessage": "Incorrect template" + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_hydrate_on_viewport_with_options.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_hydrate_on_viewport_with_options.ts new file mode 100644 index 00000000000..8240cbda7d6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_hydrate_on_viewport_with_options.ts @@ -0,0 +1,13 @@ +import {Component} from '@angular/core'; + +@Component({ + template: ` + {{message}} + @defer (hydrate on viewport({rootMargin: '123px', threshold: 59})) { + {{message}} + } + `, +}) +export class MyApp { + message = 'hello'; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_hydrate_on_viewport_with_options_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_hydrate_on_viewport_with_options_template.js new file mode 100644 index 00000000000..bb87d8a76f4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_hydrate_on_viewport_with_options_template.js @@ -0,0 +1,12 @@ +function MyApp_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtext(0); + $r3$.ɵɵdomTemplate(1, MyApp_Defer_1_Template, 1, 1); + $r3$.ɵɵdefer(2, 1, null, null, null, null, null, null, null, 1); + $r3$.ɵɵdeferHydrateOnViewport({rootMargin: "123px", threshold: 59}); + $r3$.ɵɵdeferOnIdle(); + } + if (rf & 2) { + $r3$.ɵɵtextInterpolate1(" ", ctx.message, " "); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_on_viewport_with_options.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_on_viewport_with_options.ts new file mode 100644 index 00000000000..6b7b8c5fed0 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_on_viewport_with_options.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; + +@Component({ + template: ` + {{message}} + @defer (on viewport({trigger: button, rootMargin: '123px', threshold: 59})) { + {{message}} + } @placeholder { + + } + `, +}) +export class MyApp { + message = 'hello'; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_on_viewport_with_options_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_on_viewport_with_options_template.js new file mode 100644 index 00000000000..1531e2f9e4b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_on_viewport_with_options_template.js @@ -0,0 +1,11 @@ +function MyApp_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtext(0); + $r3$.ɵɵdomTemplate(1, MyApp_Defer_1_Template, 1, 1)(2, MyApp_DeferPlaceholder_2_Template, 3, 0); + $r3$.ɵɵdefer(3, 1, null, null, 2); + $r3$.ɵɵdeferOnViewport(0, -1, {rootMargin: "123px", threshold: 59}); + } + if (rf & 2) { + $r3$.ɵɵtextInterpolate1(" ", ctx.message, " "); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_prefetch_on_viewport_with_options.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_prefetch_on_viewport_with_options.ts new file mode 100644 index 00000000000..5af2213fde1 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_prefetch_on_viewport_with_options.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; + +@Component({ + template: ` + {{message}} + @defer (prefetch on viewport({trigger: button, rootMargin: '123px', threshold: 59})) { + {{message}} + } @placeholder { + + } + `, +}) +export class MyApp { + message = 'hello'; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_prefetch_on_viewport_with_options_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_prefetch_on_viewport_with_options_template.js new file mode 100644 index 00000000000..eaa6d1ccbc3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_prefetch_on_viewport_with_options_template.js @@ -0,0 +1,12 @@ +function MyApp_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtext(0); + $r3$.ɵɵdomTemplate(1, MyApp_Defer_1_Template, 1, 1)(2, MyApp_DeferPlaceholder_2_Template, 3, 0); + $r3$.ɵɵdefer(3, 1, null, null, 2); + $r3$.ɵɵdeferPrefetchOnViewport(0, -1, {rootMargin: "123px", threshold: 59}); + $r3$.ɵɵdeferOnIdle(); + } + if (rf & 2) { + $r3$.ɵɵtextInterpolate1(" ", ctx.message, " "); + } +} diff --git a/packages/compiler/src/template/pipeline/ir/src/ops/create.ts b/packages/compiler/src/template/pipeline/ir/src/ops/create.ts index 906843a5a7a..dab3c87c8a5 100644 --- a/packages/compiler/src/template/pipeline/ir/src/ops/create.ts +++ b/packages/compiler/src/template/pipeline/ir/src/ops/create.ts @@ -1408,6 +1408,7 @@ interface DeferInteractionTrigger extends DeferTriggerWithTargetBase { interface DeferViewportTrigger extends DeferTriggerWithTargetBase { kind: DeferTriggerKind.Viewport; + options: o.Expression | null; } /** diff --git a/packages/compiler/src/template/pipeline/src/ingest.ts b/packages/compiler/src/template/pipeline/src/ingest.ts index ffd903c7252..8d005d68cb8 100644 --- a/packages/compiler/src/template/pipeline/src/ingest.ts +++ b/packages/compiler/src/template/pipeline/src/ingest.ts @@ -849,6 +849,9 @@ function ingestDeferTriggers( targetSlot: null, targetView: null, targetSlotViewSteps: null, + options: triggers.viewport.options + ? convertAst(triggers.viewport.options, unit.job, triggers.viewport.sourceSpan) + : null, }, modifier, triggers.viewport.sourceSpan, diff --git a/packages/compiler/src/template/pipeline/src/instruction.ts b/packages/compiler/src/template/pipeline/src/instruction.ts index e1796f4aed2..d7f933de3bb 100644 --- a/packages/compiler/src/template/pipeline/src/instruction.ts +++ b/packages/compiler/src/template/pipeline/src/instruction.ts @@ -375,7 +375,7 @@ const deferTriggerToR3TriggerInstructionsMap = new Map([ export function deferOn( trigger: ir.DeferTriggerKind, - args: (number | null)[], + args: o.Expression[], modifier: ir.DeferOpModifierKind, sourceSpan: ParseSourceSpan | null, ): ir.CreateOp { @@ -383,11 +383,7 @@ export function deferOn( if (instructionToCall === undefined) { throw new Error(`Unable to determine instruction for trigger ${trigger}`); } - return call( - instructionToCall, - args.map((a) => o.literal(a)), - sourceSpan, - ); + return call(instructionToCall, args, sourceSpan); } export function projectionDef(def: o.Expression | null): ir.CreateOp { diff --git a/packages/compiler/src/template/pipeline/src/phases/reify.ts b/packages/compiler/src/template/pipeline/src/phases/reify.ts index 989775e51cb..4a7149c4dc8 100644 --- a/packages/compiler/src/template/pipeline/src/phases/reify.ts +++ b/packages/compiler/src/template/pipeline/src/phases/reify.ts @@ -375,27 +375,44 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList