Today the overlay polls every 3s (setInterval in overlay.ts). Each poll runs document.querySelectorAll('*') to find component hosts, and writes a data-ng-devtools-id attribute on every host it finds. Those attributes are never removed, not even on dispose.
Proposal:
- Refresh on change detection instead of polling. Register a callback with window.ng.ɵsetProfiler (dev mode), react to the first template update of each tick, and debounce it by ~250ms outside the Angular zone. On Angular 20+ several profilers can coexist and setProfiler returns a remover. Before 20, registering one replaces any existing profiler, so keep polling as the fallback there. Never call setProfiler(null): it clears every registered profiler.
- Find roots from [ng-version] elements (plus ng.getComponent hits under document.body) and walk childNodes, instead of querying every element on the page.
- Stop writing attributes onto the app's elements. Track nodes with a Map from directive instance to id and tree position instead. Hydration isn't affected (Angular doesn't compare attributes), but the leftover attributes show up in DOM snapshot tests and never get cleaned up.
- Keep timers and listeners outside the Angular zone even if the overlay is started from inside Angular code (e.g. a lazy import in a component), by escaping via window.Zone.current. Loaded as documented, it already runs in the root zone and doesn't trigger change detection.
This touches every collector (component tree, signals, DI, NgRx, forms), so it's best done after #16 lands, on top of overlay-core.ts.
Today the overlay polls every 3s (setInterval in overlay.ts). Each poll runs document.querySelectorAll('*') to find component hosts, and writes a data-ng-devtools-id attribute on every host it finds. Those attributes are never removed, not even on dispose.
Proposal:
This touches every collector (component tree, signals, DI, NgRx, forms), so it's best done after #16 lands, on top of overlay-core.ts.