feat(devtools): add resource visualization to the signal graph#65623
feat(devtools): add resource visualization to the signal graph#65623thePunderWoman merged 2 commits intoangular:mainfrom
Conversation
a4b4bf7 to
61703bd
Compare
|
cc @milomg P.s. It seems that I can't add Milo to the reviewers. |
61703bd to
c7ccaf4
Compare
...ive-explorer/signals-view/signals-details/signals-value-tree/signals-value-tree.component.ts
Outdated
Show resolved
Hide resolved
...ive-explorer/signals-view/signals-details/signals-value-tree/signals-value-tree.component.ts
Outdated
Show resolved
Hide resolved
dgp1130
left a comment
There was a problem hiding this comment.
Question: Just to make sure I'm understanding, the way this works is by parsing the debugName value of Resource#mySigName.subNodeName and then displays a cluster of type resource named mySigName and contains a node named subNodeName. Multiple signals with the same mySigName will get included in the same resource. Is that a roughly-accurate representation?
I'm curious about the motivation to go this route, in particular I'm wondering if there might be value in doing more of this logic at the @angular/core level. The current system relies on a very specific naming convention in @angular/core and then DevTools is responsible for translating that naming convention into a useful data format. How hard would it be to make @angular/core actually emit the desired structure? For example, what if @angular/core looked something like this?
Basically I'm wondering if we can track cluster data directly in @angular/core and as a part of the implementation of resource in particular. DevTools would just be responsible for rendering that format without any special understanding of resource (beyond styling the clustered node).
I'm thinking this has a few benefits:
- It makes
ng.getSignalGrapha little more useful as it provides clustering information directly rather than asking each consumer to implement the logic extracting the naming convention into cluster information. - It supports nested clusters (doesn't seem useful at the moment and certainly doesn't need to be pulled into scope right now, but may come up in the future depending on what signal primitives we create).
- Doesn't rely on a magic naming convention and should be easier to create new clusters in the future.
- Decouples DevTools from the logic of creating clusters, all DevTools needs to do is render it.
I suspect the downside of my proposal is that it makes the implementation more difficult to land by centralizing more of it in @angular/core. We need to make sure all this code gets effectively optimized out in production, and even my example doesn't quite do that perfectly (IMHO due to this bug and this bug, but we have to work with what we've got).
WDYT? Is something like this worth exploring or would it be easier to keep this kind of logic in DevTools? We could potentially come back to this later if/when we want to generalize the pattern beyond resource or maybe what you have is better just by nature of being easier to land in the first place.
|
Answer: Yes, overall the clustering logic relies on the My motivation to go that route probably has to do with treating clusters more as a "presentation logic" rather than something else, which respectively led to my intent to offload this as much as possible from However, it's worth mentioning that if we go that course, we'll still need to have some clustering logic in the DevTools. Basically, it will be split between In other words, I definitely see the value of having clusters generated on a FW level, but that will come with some added complexity and, potentially, management, in return for a more robust clustering* that doesn't rely on the debug name. As for extension and inclusion of other compound signals – the current solution allows that to happen with minimal effort (i.e. creating a cluster identifier) given that compound signal already implements the debug name contract on So, I think that keeping everything central to DT will be easier to manage compared to splitting the logic, but I guess we should evaluate whether having clusters available for other potential * Side note: The |
dgp1130
left a comment
There was a problem hiding this comment.
No problem on the usage of the debugName convention, I'm fine to stick with this for now. Got about halfway through the PR today, I'll follow up with the rest as soon as I can.
...devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/devtools-signal-graph.spec.ts
Outdated
Show resolved
Hide resolved
| if (match.signalName === 'value') { | ||
| cluster.previewNode = i; | ||
| } |
There was a problem hiding this comment.
Observation: This scheme couples the value of the cluster node to one child node in particular. I wonder if showing the resource snapshot (or some other form of computed value) might be more appropriate as a way of representing both value and error states at the same time, but I don't think we can do that if the snapshot itself isn't a node in the cluster.
This also forces the name of that child to be value when a more descriptive name might be more appropriate in the expanded view.
It's probably fine for resource for now, I'm just calling it out as a limitation which we might want to lift in the future if we add more clusters.
There was a problem hiding this comment.
Realistically, we don't have that much space to render the values of multiple nodes as a preview; hence, my approach with previewNode. The alternative, which is already implemented, is to omit the previewNode for the resource and leave the generic and collective "[nodes]" label that's being rendered by default.
There was a problem hiding this comment.
I don't mean multiple nodes in the preview, just that we might pick another another node than value or use some data computed from multiple nodes, such as the resource snapshot, as the preview value.
{status: 'loading'}
{status: 'resolved', value: {foo: 'bar'}}
{status: 'error', error: Error { message: 'Oh noes!' }}There was a problem hiding this comment.
I guess the confusion stems from the === 'value'. This was intended only for resources since it was part of the now-gone resourceClustersIdentifier. Each ClusterIdentifier could have its own preview node, any node from the cluster.
Yeah, a resource snapshot won't be possible with this design, but I think it falls under the "not enough real estate" category. Regardless, if we unbound the preview from an actual concrete node, we'll need to introduce a more complex and cluster-specific node update logic. I suggest to not do that until there is a clear need for it.
...s/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/devtools-signal-graph.ts
Outdated
Show resolved
Hide resolved
...s/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/devtools-signal-graph.ts
Outdated
Show resolved
Hide resolved
...s/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/devtools-signal-graph.ts
Outdated
Show resolved
Hide resolved
...ects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/signal-graph-types.ts
Show resolved
Hide resolved
devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/utils.ts
Show resolved
Hide resolved
...b/devtools-tabs/directive-explorer/signals-view/signals-details/signals-details.component.ts
Outdated
Show resolved
Hide resolved
...b/devtools-tabs/directive-explorer/signals-view/signals-details/signals-details.component.ts
Outdated
Show resolved
Hide resolved
...b/devtools-tabs/directive-explorer/signals-view/signals-details/signals-details.component.ts
Outdated
Show resolved
Hide resolved
3d24191 to
907ddb9
Compare
devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/BUILD.bazel
Outdated
Show resolved
Hide resolved
...s/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/devtools-signal-graph.ts
Outdated
Show resolved
Hide resolved
907ddb9 to
6e04ad3
Compare
dgp1130
left a comment
There was a problem hiding this comment.
I'll have to defer to @AleksanderBodurri on the d3 stuff, but otherwise this looks good overall. Feel free to ignore/defer any notes about pre-existing code, as it's a bit tricky to identify for moved files.
devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/utils.ts
Outdated
Show resolved
Hide resolved
devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/utils.ts
Outdated
Show resolved
Hide resolved
...s/src/lib/devtools-tabs/directive-explorer/signals-view/signals-visualizer/dagre-d3-types.ts
Show resolved
Hide resolved
| } | ||
|
|
||
| private setUpSignalsVisualizer() { | ||
| this.signalsVisualizer = new SignalsGraphVisualizer(this.svgHost().nativeElement); |
There was a problem hiding this comment.
Consider: Could this be a computed so other things can depend on it and potentially react if svgHost changes?
There was a problem hiding this comment.
I don't think we want or intend svgHost ref changes.
This means that we'll have to get rid of setUpSignalsVisualizer and let reactivity handle the whole flow. This will conflict with your suggestion below for using non-null assertions, since we won't be able to time when the effects must be initialized. A prop makes things a bit more explicit and easier to follow, IMO.
...ools-tabs/directive-explorer/signals-view/signals-visualizer/signals-visualizer.component.ts
Outdated
Show resolved
Hide resolved
...ools-tabs/directive-explorer/signals-view/signals-visualizer/signals-visualizer.component.ts
Outdated
Show resolved
Hide resolved
|
|
||
| effect(() => { | ||
| this.element(); | ||
| // Reset the visualizer when the element changes. |
There was a problem hiding this comment.
Question: How are we using element? I don't see it referenced anywhere.
I'm wondering if there's a way to co-locate the effect with element usage, that way we don't need this empty read for timing purposes.
There was a problem hiding this comment.
It merely acts as a dependency to trigger the visualizer reset.
The visualizer lives outside of the reactive realm. It contains all of the D3 instructions for rendering the graph DOM. The only viable option to achieve what you suggest is to make that D3 code reactive, unless you have something else on your mind?
...c/lib/devtools-tabs/directive-explorer/signals-view/signals-visualizer/signals-visualizer.ts
Show resolved
Hide resolved
...c/lib/devtools-tabs/directive-explorer/signals-view/signals-visualizer/signals-visualizer.ts
Outdated
Show resolved
Hide resolved
6e04ad3 to
8dac8fc
Compare
Convert the signal graph to a Devtools-FE-specific signal graph that supports clusters; Add support for `resource` clusters; Introduce some improvements to the signal graph viz
51ad281 to
5f1fa92
Compare
devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/utils.ts
Outdated
Show resolved
Hide resolved
devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph/utils.ts
Show resolved
Hide resolved
...ools-tabs/directive-explorer/signals-view/signals-visualizer/signals-visualizer.component.ts
Show resolved
Hide resolved
5f1fa92 to
2e56952
Compare
2e56952 to
68fbf95
Compare
|
This PR was merged into the repository. The changes were merged into the following branches:
|
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
The PR introduces support for
resourcenodes to the signal graph visualization as part of #63227.Screen.Recording.2025-11-25.at.16.23.32.mov
Changes
The new changes are as follows:
toSignal,afterRenderEffect).