Repository navigation
components: fixes and improvements for components, change detection and defer blocks #344
Copy link
Copy link
Closed
Labels
bugSomething is broken or shows wrong dataSomething is broken or shows wrong datafeatureA feature request or a pull request that adds oneA feature request or a pull request that adds one
Description
Context
Fixes and improvements from a code audit of the components inspector, change detection recording, defer blocks and control flow, done in October 2026. Roadmap work lives in #340, #341, #342 and #343. Items marked (to confirm) were not reproduced; check them before fixing.
The components inspector
Problems
Medium
- Picking leaks pointer events to the app: only
clickcallspreventDefaultandstopPropagation.pointerdown,mousedown,pointerupandmouseupstill reach the page, so menus, drag handles and CDK overlays that react to them open or close while you pick. Touch picking doesn't work either. Capture those events too, outside the popup, and resolve the pick onpointerup(packages/devtools/src/component-pick.ts:58-63,97-102).
Low
- SVG component hosts can't be highlighted while picking:
onHoverkeeps the host only when it is anHTMLElement, so a component on ansvg:gselector gets no box.showHighlightalready takes anElement, so acceptElementhere (packages/devtools/src/component-pick.ts:47). - Highlight box ignores shadow-root children: when a host has an empty rect,
highlightBoxonly walksel.children. Adisplay: contentshost whose content is in its shadow root gets no box. Also walkel.shadowRoot?.children(packages/devtools/src/page-highlight.ts:32). - No source location for directives, and the fallback can be wrong:
componentSourcereads onlyɵcmp.debugInfo, so the directive entries in the detail never get a file. WithoutdebugInfo,sourceForfalls back tomatches[0]when two classes share a name, and the panel shows that file as if it were certain. Readɵdir.debugInfotoo, fillsourceon each directive in the detail, and mark guessed locations as "likely" (packages/devtools/src/component-source.ts:17,packages/devtools/src/component-tree.ts:275-283,app/src/pages/component-tree.ts:1695-1699). - Changed values aren't visible: properties are snapshots, so you can't tell what changed between ticks, and a
kind: 'signal'property has no link to the Signals page. Flash values that changed since the last detail, and add a "Show in Signals" action for signal properties (app/src/pages/component-tree.ts,packages/devtools/src/component-tree.ts).
Improvements
- Open the template file and line as well as the class. The source scan doesn't return
templateUrlfor components yet, so add it to the scan and toSourceComponent(packages/devtools/src/rpc/get-components.ts,app/src/pages/component-tree.ts:36-46). - Pick by keyboard: once picking, arrow keys move between component hosts on the page and Enter selects (
packages/devtools/src/component-pick.ts). - "Copy as JSON" and "Copy host path" actions on the selected component, for bug reports and agents (
app/src/pages/component-tree.ts).
Already tracked elsewhere
- Editing inputs, properties and signals: phase 2 of 4: deeper inspection #341.
- Open in editor outside the extension, and Alt+click in pick mode: phase 1 of 4: safe by default and wired into your workflow #340.
- Console handles (
$ng0, "Store as global"): phase 2 of 4: deeper inspection #341. - Full tree re-collected on every tick, MutationObserver dirty flag: phase 3 of 4: a shell you live in #342.
- Directive-only hosts as tree nodes: phase 2 of 4: deeper inspection #341.
- Lazy expansion of nested values beyond depth 3 and 30 keys, plus a property filter: phase 2 of 4: deeper inspection #341.
Change detection recording
Problems
Medium
- A second start wipes a running recording:
setRecording(true)callsrecorder.start()every time, andstart()callsreset(). An agentrecord: "start"with nopageis broadcast to every tab, so it erases the user's panel recording. A double click does the same. Makeon: truea no-op when already recording, and clear only onclear: true. Consider requiringpagefor agent start and clear when more than one tab is connected (packages/devtools/src/cd-overlay.ts:56-61,packages/devtools/src/cd-recorder.ts:228-232,packages/devtools/src/devframe.ts:1949-1954). - Stale output trigger:
lastOutputstays set until the nextChangeDetectionStart. An output that started no tick (zoneless with no signal change, or an output nobody listens to) is then credited to a later tick caused by a timer, HTTP or a signal. StamplastOutputwithnow()and drop it when change detection starts more than about one frame later, or clear it in a microtask after the output ends (packages/devtools/src/cd-recorder.ts:119-132).
Low
- Wrong "needs Angular 20" message:
supportedbecomesfalsewheneveraddProfilerListenerreturnsnull. That also happens whenngis not available yet, when the page has no[ng-version](createApplication, Angular Elements, whereangularMajorreturns 0), or whenɵsetProfilerthrows. The panel and the agent tool then show a fixed "needs Angular 20" text, and nothing retries until the user toggles again. Report a reason (no-ng-yet,angular<20,no-profiler-api), retry the attach on the next refresh while recording is wanted, and read the version another way whenng-versionis missing (packages/devtools/src/cd-overlay.ts:58-60,packages/devtools/src/change-detection.ts:55-59,app/src/pages/cd-recording.ts:66-68,packages/devtools/src/rpc/cd-tools.ts:161-163). - Start is confirmed without an answer from the page:
change-detection-recordis broadcast withoptional: true, and the panel and agent tool say "Recording started" as soon as the hub accepts the request. "Could not reach the page" only shows when the hub itself fails. Wait up to about 1 s for a push withrecording: true(orsupported: false) from thatpageId, and say when the page did not answer (packages/devtools/src/devframe.ts:1256-1263,packages/devtools/src/devframe.ts:1955-1961,app/src/pages/cd-recording.ts:313-332). - Limits are hit silently: totals stop taking new classes at 1000, host
WeakRefs stop at 2000 instances, and a failedgetHostElementis cached asnullforever. Tree badges and totals then go missing with no note, unlike cycles, which carrydropped. AdddroppedClassesanddroppedHoststoCdRecording, show them inLimitNoteand the agent text, and do not cache anullhost id (packages/devtools/src/cd-recorder.ts:165,packages/devtools/src/cd-recorder.ts:180-187,packages/devtools/src/cd-overlay.ts:42-54). - Nested tick replaces the cycle in progress: a
ChangeDetectionStartwhilecurrentis set overwrites it and resets the frame stack, so the outer cycle is lost. Keep a cycle stack, or count a nested tick as a pass (packages/devtools/src/cd-recorder.ts:122-133) (to confirm). - Panel shows less than the agent tool: the agent answer has a "Most often checked" list, the panel does not. Cycle rows can't be expanded to their top 10
components, component names don't link to their tree row or highlight, and the zone mode is not shown next to Record. The slowest table usestrack $indexinstead ofc.name(app/src/pages/cd-recording.ts:75-126,app/src/pages/cd-recording.ts:89,packages/devtools/src/rpc/cd-tools.ts:171-186). - Duplicated types: the panel declares
CdCheck,CdCycle,CdComponentStatandCdPageagain instead of importing them, so the copies can drift without a compile error. Export them from@pangular-inspector/devtools/config(or a shared types entry), aslimit-note.tsandnetwork-inspector.tsalready do (app/src/pages/cd-recording.ts:8-38,packages/devtools/src/cd-recorder.ts:17-53,packages/devtools/src/cd-overlay.ts:18-22).
Improvements
- Keep recording across a reload: store a "recording wanted" flag in
sessionStorageperpageId, likestoredInstrumented()does for the router, so a reload profile with bootstrap and the first cycles can be captured (packages/devtools/src/cd-overlay.ts,packages/devtools/src/router-actions.ts:91). - Slow cycle budget: add
limits.cdSlowMs(default 16 ms). Highlight cycles over budget in the panel and count them in the agent answer, so an agent can check "no frame-dropping ticks" after a change (packages/devtools/src/config.ts,packages/devtools/src/rpc/cd-tools.ts,app/src/pages/cd-recording.ts).
Already tracked elsewhere
- Push deltas instead of the full recording on every refresh: phase 3 of 4: a shell you live in #342 (performance and scale).
- Lifecycle hook and
afterRendertiming (profiler events 4/5 and 16/17): phase 2 of 4: deeper inspection #341. - Flame graph, cycle bar chart with a brush, export and import, Chrome Performance track, live flash overlay: phase 2 of 4: deeper inspection #341.
- Naming the trigger source (
Zone.currentTask.source, zoneless notify sources), OnPush and wasted-check hints, before/after session comparison: phase 4 of 4: deeper diagnosis #343.
Defer blocks and control flow
Problems
Medium
- Stuck placeholder noise:
deferNotesflags every block that sits on its placeholder for 10s as "Needs attention", even with aviewport,interactionorhovertrigger, where waiting is normal. Only flagidle,immediate,timer,whenor no triggers, and give the others a plain "waiting for trigger" note outside the attention list (packages/devtools/src/rpc/component-tools.ts:121-136,164). - No runtime
@let:@letvalues never show in component detail; the only handling is a source regex. If the debug API exposes@letslots in the component's LView, list them read-only throughserializeand redaction; otherwise document the gap incomponents.md(packages/devtools/src/rpc/get-pipes.ts:333) (to confirm).
Low
- Missing
hydratedAt: it is set only when the tracker saw the block dehydrated on an earlier read, so blocks that hydrate before the first collect, or between two reads, never get a time. Mark blocks first seen hydrated as "hydrated before inspection", or catch the transition another way (packages/devtools/src/defer-blocks.ts:96-100). - Silent 500-block cap:
collectreturns early atMAX_BLOCKSand the server slices to 500 again, with no flag. Return atruncatedflag and say so indeferBlocksTextand the panel pill (packages/devtools/src/defer-blocks.ts:79,packages/devtools/src/rpc/component-tools.ts:42). - Unfocusable rows: rows without an owner use
[disabled], so keyboard users can't focus them to highlight the block while mouse users can hover them. Usearia-disabledwith a guard inshowOwner(app/src/pages/component-tree.ts:521-530). - Panel hides data it has: the panel's
DeferBlocktype dropssince,hydratedAt,loading/placeholdertimings,hasErrorBlockand the notes the agent tool computes, and the section is hidden both when there are no blocks and when the util is missing. Add an expandable row detail, reusedeferNotesfor badges, and show a one-line notice whendeferBlocksis undefined (app/src/pages/component-tree.ts:87-95,513-548). - Clock skew:
deferBlocksTextcompares the server'sDate.now()withsincefrom the page clock, so ages are wrong on remote or Angular Native devices whose clock differs. Send the page'snow()with each report, or an age per block (packages/devtools/src/rpc/component-tools.ts:123-125,packages/devtools/src/devframe.ts:1917) (to confirm). - Mismatch "component" is a node name: hydration mismatches set
componenttonode.nodeName, for exampledivor#text. Resolve the owning component and send the node name, component class and element id (packages/devtools/src/http-hydration.ts:59). - Partial block highlight:
highlightBlockuses onlyrootIds[0], so blocks with several roots are under-highlighted. Letrequest-page-highlighttake several ids and draw the union (app/src/pages/component-tree.ts:1921-1923,packages/devtools/src/defer-blocks.ts:111-114).
Improvements
- Badges on component tree nodes for the control flow blocks they own ("2 defer, 1 for") (
app/src/pages/component-tree.ts,packages/devtools/src/defer-blocks.ts). - An action tool to trigger or force-load a defer block, like
DeferBlockFixture.render(state), listed inACTION_TOOLS(packages/devtools/src/devframe.ts,packages/devtools/src/config.ts). - Record defer state transitions with timestamps and add them as a layer of the timeline in phase 3 of 4: a shell you live in #342, so chunk loads tie to defer triggers (
packages/devtools/src/defer-blocks.ts). - Link hydration mismatches on the Network page to the component tree: click to select and highlight the owner (
packages/devtools/src/http-hydration.ts,app/src/pages). - Include the defer blocks a component owns in the
pangular:component-treedetail of that instance, so agents need one call (packages/devtools/src/rpc/component-tools.ts).
Already tracked elsewhere
@forloop support (count,trackexpression,@empty, panel section and agent tool): phase 2 of 4: deeper inspection #341.- Hydration overlay on the page for hydrated, skipped, mismatched and dehydrated regions: phase 2 of 4: deeper inspection #341.
Generated by Claude Code
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething is broken or shows wrong dataSomething is broken or shows wrong datafeatureA feature request or a pull request that adds oneA feature request or a pull request that adds one
Activity
docs: point the roadmap at the new tracking issues
docs: point the roadmap at the new tracking issues
docs: add a roadmap page (#339)
fix: audit findings for components, injectors, signals, pipes and HTTP (