From 1d431a9637eabf0b0eebdc4ac2d5c7ae7de7583c Mon Sep 17 00:00:00 2001 From: hawkgs Date: Wed, 3 Dec 2025 15:51:00 +0200 Subject: [PATCH] fix(devtools): router tree not being rendered Convert the `TreeVisualizer` to a signal, in the `TreeVisualizerComponent`, to ensure that the router tree render effect is always called; Use explicit `afterNextRender` phases. --- .../tree-visualizer.component.ts | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts b/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts index 2943a95e824..ffac5a99248 100644 --- a/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts @@ -56,19 +56,23 @@ export class TreeVisualizerComponent { protected readonly nodeMouseover = output>(); readonly panning = signal(false); + private readonly visualizer = signal | null>(null); private initialRender: boolean = true; - private visualizer?: TreeVisualizer; constructor() { - afterNextRender(() => { - this.visualizer?.cleanup(); - this.visualizer = new TreeVisualizer( - this.container().nativeElement, - this.group().nativeElement, - this.config(), - ); - this.ready.emit(); + afterNextRender({ + write: () => this.visualizer()?.cleanup(), // Cleans up the visualization DOM + read: () => { + this.visualizer.set( + new TreeVisualizer( + this.container().nativeElement, + this.group().nativeElement, + this.config(), + ), + ); + this.ready.emit(); + }, }); effect(() => { @@ -76,7 +80,7 @@ export class TreeVisualizerComponent { }); inject(DestroyRef).onDestroy(() => { - this.visualizer?.dispose(); + this.visualizer()?.dispose(); }); } @@ -85,26 +89,27 @@ export class TreeVisualizerComponent { } snapToRoot(scale?: number) { - this.visualizer?.snapToRoot(scale); + this.visualizer()?.snapToRoot(scale); } snapToNode(node: T, scale?: number) { - this.visualizer?.snapToNode(node, scale); + this.visualizer()?.snapToNode(node, scale); } getNodeById(id: string) { - return this.visualizer?.getInternalNodeById(id); + return this.visualizer()?.getInternalNodeById(id); } private renderGraph(root: T): void { - if (!this.visualizer) { + const visualizer = this.visualizer(); + if (!visualizer) { return; } - this.visualizer.render(root); - this.visualizer.onNodeClick((_, node) => this.nodeClick.emit(node)); - this.visualizer.onNodeMouseout((_, node) => this.nodeMouseout.emit(node)); - this.visualizer.onNodeMouseover((_, node) => this.nodeMouseover.emit(node)); + visualizer.render(root); + visualizer.onNodeClick((_, node) => this.nodeClick.emit(node)); + visualizer.onNodeMouseout((_, node) => this.nodeMouseout.emit(node)); + visualizer.onNodeMouseover((_, node) => this.nodeMouseover.emit(node)); this.render.emit({initial: this.initialRender}); if (this.initialRender) {