Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
ɵDeferBlockData as DeferBlockData,
ɵHydratedNode as HydrationNode,
} from '@angular/core';
import {CurrentDeferBlock, HydrationStatus} from '../../../../protocol';
import {RenderedDeferBlock, HydrationStatus} from '../../../../protocol';

import {ComponentTreeNode} from '../interfaces';
import {ngDebugClient} from '../ng-debug-api/ng-debug-api';
Expand Down Expand Up @@ -126,14 +126,19 @@ function groupDeferChildrenIfNeeded(
getDirectiveMetadata?: FrameworkAgnosticGlobalUtils['getDirectiveMetadata'],
) {
const currentDeferBlock = deferBlocks.currentBlock;
const isFirstDefferedChild = node === currentDeferBlock?.rootNodes[0];
if (isFirstDefferedChild) {
const isFirstDeferredChild = node === currentDeferBlock?.rootNodes[0];
// Handles the case where the @defer is still unresolved but doesn't
// have a placeholder, for instance, by which children we mark
// the position of the block normally. In this case, we use the host.
const isHostNode = node === currentDeferBlock?.hostNode;

if (isFirstDeferredChild || isHostNode) {
deferBlocks.advance();

// When encountering the first child of a defer block
// We create a synthetic TreeNode reprensenting the defer block
// When encountering the first child of a defer block (or the host node),
// we create a synthetic TreeNode representing the defer block.
const childrenTree: ComponentTreeNode[] = [];
currentDeferBlock.rootNodes.forEach((child) => {
for (const child of currentDeferBlock.rootNodes) {
extractViewTree(
child,
childrenTree,
Expand All @@ -143,7 +148,7 @@ function groupDeferChildrenIfNeeded(
getDirectives,
getDirectiveMetadata,
);
});
}

const deferBlockTreeNode = {
children: childrenTree,
Expand All @@ -155,7 +160,7 @@ function groupDeferChildrenIfNeeded(
defer: {
id: `deferId-${rootId}-${deferBlocks.currentIndex}`,
state: currentDeferBlock.state,
currentBlock: currentBlock(currentDeferBlock),
renderedBlock: getRenderedBlock(currentDeferBlock),
triggers: groupTriggers(currentDeferBlock.triggers),
blocks: {
hasErrorBlock: currentDeferBlock.hasErrorBlock,
Expand Down Expand Up @@ -213,12 +218,16 @@ function groupTriggers(triggers: string[]) {
return {defer, hydrate, prefetch};
}

function currentBlock(deferBlock: DeferBlockData): CurrentDeferBlock | null {
function getRenderedBlock(deferBlock: DeferBlockData): RenderedDeferBlock | null {
if (['placeholder', 'loading', 'error'].includes(deferBlock.state)) {
return deferBlock.state as 'placeholder' | 'loading' | 'error';
}
if (deferBlock.state === 'complete') {
return 'defer';
}
return null;
}

export class RTreeStrategy {
supports(): boolean {
return (['getDirectiveMetadata', 'getComponent'] as const).every(
Expand Down Expand Up @@ -253,7 +262,7 @@ class DeferBlocksIterator {
this.currentIndex++;
}

get currentBlock() {
get currentBlock(): DeferBlockData | undefined {
return this.blocks[this.currentIndex];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,22 @@
</div>

@if (node().onPush) {
<span class="on-push">OnPush</span>
<span class="trait">OnPush</span>
}

@let defer = node().defer;

@if (!defer && (!hydration || hydration.status !== 'dehydrated')) {
<!-- Shown/hidden via CSS -->
<span class="console-reference"> == $ng0 </span>
<span class="console-reference trait"> == $ng0 </span>
}

@if (defer && defer.currentBlock) {
<span class="on-push">(&#64;{{ defer.currentBlock }})</span>
@if (defer) {
@if (defer.renderedBlock && defer.renderedBlock !== 'defer') {
<span class="trait">(@{{ defer.renderedBlock }})</span>
} @else if (!defer.renderedBlock) {
<span class="trait">(non-rendered)</span>
}
}

@switch (hydration?.status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@
display: none;
}

.console-reference,
.on-push {
.trait {
color: var(--color-tree-node-console-ref);
padding-left: 8px;
font-style: italic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ describe('TreeNodeComponent', () => {
});
await fixture.whenStable();

onPush = fixture.debugElement.query(By.css('.on-push'));
onPush = fixture.debugElement.query(By.css('.trait'));

expect(onPush).toBeTruthy();
expect(onPush.nativeElement.textContent).toEqual('OnPush');
});

it('should handle selection', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
<mat-toolbar>Current block</mat-toolbar>
<mat-toolbar>Rendered block</mat-toolbar>
<div class="defer-details">
<span class="pill"> &#64;{{ defer().currentBlock ?? 'defer' }}</span>
</div>

@let triggers = defer().triggers;
@let blocks = defer().blocks;
<mat-toolbar>Declared blocks</mat-toolbar>
<div class="defer-details">
@if (blocks.placeholderBlock) {
<span class="pill">
&#64;placeholder<!--
-->{{
blocks.placeholderBlock.minimumTime
? `(minimum ${blocks.placeholderBlock.minimumTime} ms)`
: ''
}}</span
>
}
@if (blocks.loadingBlock) {
<span class="pill">&#64;loading{{ loadingBlockInfo() }}</span>
}
@if (blocks.hasErrorBlock) {
<span class="pill">&#64;error</span>
@if (defer().renderedBlock) {
<span class="pill"> &#64;{{ defer().renderedBlock }}</span>
} @else {
<em>Nothing rendered yet</em>
}
</div>

@if (hasDeclaredBlocks()) {
@let blocks = defer().blocks;
<mat-toolbar>Declared blocks</mat-toolbar>
<div class="defer-details">
@if (blocks.placeholderBlock.exists) {
<span class="pill">
&#64;placeholder<!--
-->{{
blocks.placeholderBlock.minimumTime
? `(minimum ${blocks.placeholderBlock.minimumTime} ms)`
: ''
}}</span
>
}
@if (blocks.loadingBlock.exists) {
<span class="pill">&#64;loading{{ loadingBlockInfo() }}</span>
}
@if (blocks.hasErrorBlock) {
<span class="pill">&#64;error</span>
}
</div>
}

@let triggers = defer().triggers;
<mat-toolbar>Triggers</mat-toolbar>
<div class="defer-details">
<h3>Defer triggers</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class DeferViewComponent {

readonly loadingBlockInfo = computed(() => {
const loadingBlock = this.defer().blocks.loadingBlock;
if (!loadingBlock) {
if (!loadingBlock.exists) {
return null;
}

Expand All @@ -35,4 +35,9 @@ export class DeferViewComponent {
}
return info.length ? `(${info.join(', ')})` : null;
});

readonly hasDeclaredBlocks = computed(() => {
const blocks = this.defer().blocks;
return blocks.hasErrorBlock || blocks.placeholderBlock.exists || blocks.loadingBlock.exists;
});
}
8 changes: 4 additions & 4 deletions devtools/projects/protocol/src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ export type HydrationStatus =
actualNodeDetails: string | null;
};

export type CurrentDeferBlock = 'placeholder' | 'loading' | 'error';
export type RenderedDeferBlock = 'defer' | 'placeholder' | 'loading' | 'error';

export interface DeferInfo {
id: string;
state: 'placeholder' | 'loading' | 'complete' | 'error' | 'initial';
currentBlock: CurrentDeferBlock | null;
renderedBlock: RenderedDeferBlock | null;
triggers: {
defer: string[];
hydrate: string[];
Expand All @@ -94,8 +94,8 @@ export interface DeferInfo {

export interface BlockDetails {
hasErrorBlock: boolean;
placeholderBlock: null | {minimumTime: number | null};
loadingBlock: null | {minimumTime: number | null; afterTime: number | null};
placeholderBlock: {exists: boolean; minimumTime: number | null};
loadingBlock: {exists: boolean; minimumTime: number | null; afterTime: number | null};
}

// TODO: refactor to remove nativeElement as it is not serializable
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/render3/util/defer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {assertLView} from '../assert';
import {collectNativeNodes} from '../collect_native_nodes';
import {getLContext} from '../context_discovery';
import {CONTAINER_HEADER_OFFSET, NATIVE} from '../interfaces/container';
import {INJECTOR, LView, TVIEW} from '../interfaces/view';
import {HOST, INJECTOR, LView, TVIEW} from '../interfaces/view';
import {getNativeByTNode} from './view_utils';

/** Retrieved information about a `@defer` block. */
Expand Down Expand Up @@ -65,6 +65,9 @@ export interface DeferBlockData {
/** Stringified version of the block's triggers. */
triggers: string[];

/** The comment host/container node next to which all of the root nodes are rendered. */
hostNode: Node;

/** Element root nodes that are currently being shown in the block. */
rootNodes: Node[];
}
Expand Down Expand Up @@ -156,6 +159,7 @@ function findDeferBlocks(node: Node, lView: LView, results: DeferBlockData[]) {
minimumTime: tDetails.placeholderBlockConfig?.[MINIMUM_SLOT] ?? null,
},
triggers: tDetails.debug?.triggers ? Array.from(tDetails.debug.triggers).sort() : [],
hostNode: details.lContainer[HOST] as Node,
rootNodes,
};

Expand Down
20 changes: 20 additions & 0 deletions packages/core/test/acceptance/defer_utils_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,26 @@ describe('@defer debugging utilities', () => {
await block.render(DeferBlockState.Complete);
});

it('should return the host comment node of the currently-rendered block', () => {
@Component({
template: `
@defer (when false) {
Loaded
}
`,
})
class App {}

const fixture = TestBed.createComponent(App);
fixture.detectChanges();

const results = getDeferBlocks(fixture.nativeElement);

expect(results.length).toBe(1);
expect(results[0].hostNode).toBeTruthy();
expect(stringifyNodes([results[0].hostNode])).toEqual(['Comment(container)']);
});

function stringifyNodes(nodes: Node[]): string[] {
return nodes.map((node) => {
switch (node.nodeType) {
Expand Down