Skip to content

Commit 73c5d1c

Browse files
dylhunnalxhub
authored andcommitted
refactor(compiler): Implement the remaining defer on triggers (angular#52387)
The previous commits provided the scaffolding for `defer on`. In this commit, we build on that work, adding triggers for `immediate`, `timer`, `hover`, and `viewport`. PR Close angular#52387
1 parent 7968449 commit 73c5d1c

File tree

7 files changed

+106
-16
lines changed

7 files changed

+106
-16
lines changed

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@
6868
],
6969
"failureMessage": "Incorrect template"
7070
}
71-
],
72-
"skipForTemplatePipeline": true
71+
]
7372
},
7473
{
7574
"description": "should generate a deferred block with local dependencies",

packages/compiler/src/template/pipeline/ir/src/enums.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,5 +527,9 @@ export enum Namespace {
527527
*/
528528
export enum DeferTriggerKind {
529529
Idle,
530+
Immediate,
531+
Timer,
532+
Hover,
530533
Interaction,
534+
Viewport,
531535
}

packages/compiler/src/template/pipeline/ir/src/ops/create.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -718,13 +718,7 @@ interface DeferTriggerBase {
718718
kind: DeferTriggerKind;
719719
}
720720

721-
interface DeferIdleTrigger extends DeferTriggerBase {
722-
kind: DeferTriggerKind.Idle;
723-
}
724-
725-
interface DeferInteractionTrigger extends DeferTriggerBase {
726-
kind: DeferTriggerKind.Interaction;
727-
721+
interface DeferTriggerWithTargetBase extends DeferTriggerBase {
728722
targetName: string|null;
729723

730724
/**
@@ -746,10 +740,37 @@ interface DeferInteractionTrigger extends DeferTriggerBase {
746740
targetSlotViewSteps: number|null;
747741
}
748742

743+
interface DeferIdleTrigger extends DeferTriggerBase {
744+
kind: DeferTriggerKind.Idle;
745+
}
746+
747+
interface DeferImmediateTrigger extends DeferTriggerBase {
748+
kind: DeferTriggerKind.Immediate;
749+
}
750+
751+
interface DeferHoverTrigger extends DeferTriggerWithTargetBase {
752+
kind: DeferTriggerKind.Hover;
753+
}
754+
755+
interface DeferTimerTrigger extends DeferTriggerBase {
756+
kind: DeferTriggerKind.Timer;
757+
758+
delay: number;
759+
}
760+
761+
interface DeferInteractionTrigger extends DeferTriggerWithTargetBase {
762+
kind: DeferTriggerKind.Interaction;
763+
}
764+
765+
interface DeferViewportTrigger extends DeferTriggerWithTargetBase {
766+
kind: DeferTriggerKind.Viewport;
767+
}
768+
749769
/**
750770
* The union type of all defer trigger interfaces.
751771
*/
752-
export type DeferTrigger = DeferIdleTrigger|DeferInteractionTrigger;
772+
export type DeferTrigger = DeferIdleTrigger|DeferImmediateTrigger|DeferTimerTrigger|
773+
DeferHoverTrigger|DeferInteractionTrigger|DeferViewportTrigger;
753774

754775
export interface DeferOnOp extends Op<CreateOp> {
755776
kind: OpKind.DeferOn;

packages/compiler/src/template/pipeline/src/ingest.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock
375375

376376
// Configure all defer `on` conditions.
377377

378+
// TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will
379+
// make it easier to refactor prefetch behavior in the future.
378380
let prefetch = false;
379381
let deferOnOps: ir.DeferOnOp[] = [];
380382
for (const triggers of [deferBlock.triggers, deferBlock.prefetchTriggers]) {
@@ -383,6 +385,30 @@ function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock
383385
ir.createDeferOnOp(deferXref, {kind: ir.DeferTriggerKind.Idle}, prefetch, null!);
384386
deferOnOps.push(deferOnOp);
385387
}
388+
if (triggers.immediate !== undefined) {
389+
const deferOnOp =
390+
ir.createDeferOnOp(deferXref, {kind: ir.DeferTriggerKind.Immediate}, prefetch, null!);
391+
deferOnOps.push(deferOnOp);
392+
}
393+
if (triggers.timer !== undefined) {
394+
const deferOnOp = ir.createDeferOnOp(
395+
deferXref, {kind: ir.DeferTriggerKind.Timer, delay: triggers.timer.delay}, prefetch, null!
396+
);
397+
deferOnOps.push(deferOnOp);
398+
}
399+
if (triggers.hover !== undefined) {
400+
const deferOnOp = ir.createDeferOnOp(
401+
deferXref, {
402+
kind: ir.DeferTriggerKind.Hover,
403+
targetName: triggers.hover.reference,
404+
targetXref: null,
405+
targetSlot: null,
406+
targetView: null,
407+
targetSlotViewSteps: null,
408+
},
409+
prefetch, null!);
410+
deferOnOps.push(deferOnOp);
411+
}
386412
if (triggers.interaction !== undefined) {
387413
const deferOnOp = ir.createDeferOnOp(
388414
deferXref, {
@@ -396,11 +422,27 @@ function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock
396422
prefetch, null!);
397423
deferOnOps.push(deferOnOp);
398424
}
425+
if (triggers.viewport !== undefined) {
426+
const deferOnOp = ir.createDeferOnOp(
427+
deferXref, {
428+
kind: ir.DeferTriggerKind.Viewport,
429+
targetName: triggers.viewport.reference,
430+
targetXref: null,
431+
targetSlot: null,
432+
targetView: null,
433+
targetSlotViewSteps: null,
434+
},
435+
prefetch, null!);
436+
deferOnOps.push(deferOnOp);
437+
}
438+
// If no (non-prefetching) defer triggers were provided, default to `idle`.
439+
if (deferOnOps.length === 0) {
440+
deferOnOps.push(
441+
ir.createDeferOnOp(deferXref, {kind: ir.DeferTriggerKind.Idle}, false, null!));
442+
}
399443
prefetch = true;
400444
}
401-
if (deferOnOps.length === 0) {
402-
deferOnOps.push(ir.createDeferOnOp(deferXref, {kind: ir.DeferTriggerKind.Idle}, false, null!));
403-
}
445+
404446
unit.create.push(deferOnOps);
405447
}
406448

packages/compiler/src/template/pipeline/src/instruction.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ export function text(
186186
export function defer(
187187
selfSlot: number, primarySlot: number, dependencyResolverFn: null, loadingSlot: number|null,
188188
placeholderSlot: number|null, errorSlot: number|null, loadingConfig: o.Expression|null,
189-
placeholderConfig: o.Expression|null, sourceSpan: ParseSourceSpan|null): ir.CreateOp {
189+
placeholderConfig: o.Expression|null, enableTimerScheduling: boolean,
190+
sourceSpan: ParseSourceSpan|null): ir.CreateOp {
190191
const args: Array<o.Expression> = [
191192
o.literal(selfSlot),
192193
o.literal(primarySlot),
@@ -196,6 +197,7 @@ export function defer(
196197
o.literal(errorSlot),
197198
loadingConfig ?? o.literal(null),
198199
placeholderConfig ?? o.literal(null),
200+
enableTimerScheduling ? o.importExpr(Identifiers.deferEnableTimerScheduling) : o.literal(null),
199201
];
200202

201203
let expr: o.Expression;
@@ -209,10 +211,19 @@ export function defer(
209211

210212
const deferTriggerToR3TriggerInstructionsMap = new Map([
211213
[ir.DeferTriggerKind.Idle, [Identifiers.deferOnIdle, Identifiers.deferPrefetchOnIdle]],
214+
[
215+
ir.DeferTriggerKind.Immediate,
216+
[Identifiers.deferOnImmediate, Identifiers.deferPrefetchOnImmediate]
217+
],
218+
[ir.DeferTriggerKind.Timer, [Identifiers.deferOnTimer, Identifiers.deferPrefetchOnTimer]],
219+
[ir.DeferTriggerKind.Hover, [Identifiers.deferOnHover, Identifiers.deferPrefetchOnHover]],
212220
[
213221
ir.DeferTriggerKind.Interaction,
214222
[Identifiers.deferOnInteraction, Identifiers.deferPrefetchOnInteraction]
215223
],
224+
[
225+
ir.DeferTriggerKind.Viewport, [Identifiers.deferOnViewport, Identifiers.deferPrefetchOnViewport]
226+
],
216227
]);
217228

218229
export function deferOn(

packages/compiler/src/template/pipeline/src/phases/defer_resolve_targets.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,12 @@ export function phaseDeferResolveTargets(job: ComponentCompilationJob): void {
4646
placeholderView: ir.XrefId|null): void {
4747
switch (op.trigger.kind) {
4848
case ir.DeferTriggerKind.Idle:
49+
case ir.DeferTriggerKind.Immediate:
50+
case ir.DeferTriggerKind.Timer:
4951
return;
52+
case ir.DeferTriggerKind.Hover:
5053
case ir.DeferTriggerKind.Interaction:
54+
case ir.DeferTriggerKind.Viewport:
5155
if (op.trigger.targetName === null) {
5256
return;
5357
}

packages/compiler/src/template/pipeline/src/phases/reify.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,21 +141,30 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList<ir.CreateOp
141141
}
142142
break;
143143
case ir.OpKind.Defer:
144+
const timerScheduling =
145+
!!op.loadingMinimumTime || !!op.loadingAfterTime || !!op.placeholderMinimumTime;
144146
ir.OpList.replace(
145147
op,
146148
ng.defer(
147149
op.slot.slot!, op.mainSlot.slot!, null, op.loadingSlot?.slot ?? null,
148150
op.placeholderSlot?.slot! ?? null, op.errorSlot?.slot ?? null, op.loadingConfig,
149-
op.placeholderConfig, op.sourceSpan));
151+
op.placeholderConfig, timerScheduling, op.sourceSpan));
150152
break;
151153
case ir.OpKind.DeferOn:
152154
let args: number[] = [];
153155
switch (op.trigger.kind) {
154156
case ir.DeferTriggerKind.Idle:
157+
case ir.DeferTriggerKind.Immediate:
158+
break;
159+
case ir.DeferTriggerKind.Timer:
160+
args = [op.trigger.delay];
155161
break;
156162
case ir.DeferTriggerKind.Interaction:
163+
case ir.DeferTriggerKind.Hover:
164+
case ir.DeferTriggerKind.Viewport:
157165
if (op.trigger.targetSlot?.slot == null || op.trigger.targetSlotViewSteps === null) {
158-
throw new Error('Slot or view steps not set in trigger reification');
166+
throw new Error(`Slot or view steps not set in trigger reification for trigger kind ${
167+
op.trigger.kind}`);
159168
}
160169
args = [op.trigger.targetSlot.slot];
161170
if (op.trigger.targetSlotViewSteps !== 0) {

0 commit comments

Comments
 (0)