Skip to content

Watch signal#69800

Open
csmick wants to merge 2 commits into
angular:mainfrom
csmick:watch-signal
Open

Watch signal#69800
csmick wants to merge 2 commits into
angular:mainfrom
csmick:watch-signal

Conversation

@csmick

@csmick csmick commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.dev application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

There is no way to "watch" a signal (log the new value programmatically on change) in Angular.

Issue Number: #62236

What is the new behavior?

There is a button in the signal graph details pane in devtools that allows the user to "watch" or "unwatch" a signal.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

@pullapprove
pullapprove Bot requested review from atscott and crisbeto July 15, 2026 21:16
@angular-robot angular-robot Bot added detected: feature PR contains a feature commit area: core Issues related to the framework runtime area: devtools labels Jul 15, 2026
@ngbot ngbot Bot added this to the Backlog milestone Jul 15, 2026
@angular-robot angular-robot Bot added the requires: TGP This PR requires a passing TGP before merging is allowed label Jul 15, 2026
@JeanMeche

Copy link
Copy Markdown
Member

That would address #62236 btw !

Comment thread packages/core/src/render3/util/signal_debug.ts
@csmick
csmick force-pushed the watch-signal branch 4 times, most recently from 8f1114d to b4340e4 Compare July 15, 2026 22:23
@hawkgs hawkgs linked an issue Jul 16, 2026 that may be closed by this pull request
1 task
@csmick
csmick requested a review from dgp1130 July 16, 2026 17:27
Comment thread packages/core/src/render3/util/global_utils.ts
Comment thread packages/core/src/render3/util/signal_debug.ts Outdated
Comment thread packages/core/src/render3/util/signal_debug.ts Outdated
Comment thread packages/core/src/render3/util/signal_debug.ts Outdated
Comment on lines +95 to +97
interface InternalCoreGlobalUtils {
toggleWatchSignal(id: string): void;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: I'm wondering the abstraction between DevTools and core should include the string signal ID or directly keep a reference to the raw signal value? I know eventually we need to convert it into a serializable ID, but should that be done at the core or DevTools layers?

For example, an alternative approach might look like:

// core/...

type Dispose = () => void;
interface InternalCoreGlobalUtils {
  watchSignal(node: ReactiveNode): Dispose;
}

function watchSignal(node: ReactiveNode): Dispose {
  const watch = createWatch(...);
  return () => void watch.destroy();
}

Then DevTools can manage the IDs and maps:

// devtools/...

const watchedSignals = new Map<string, WeakRef<{dispose: Dispose}>>();
function toggleWatchSignal(id: string): void {
  const watched = watchedSignals.get(id);
  if (watched) {
    watched.dispose();
    watchedSignals.delete(id);
  } else {
    const stopWatching = ng.watchSignal(getSignalById(id));
    watchedSignals.set(id, new WeakRef({dispose: stopWatching}));
  }
}

The potential benefits from my perspective are:

  1. Cleaner abstraction between core and DevTools.
  2. Less complexity in core, given that DevTools is the only consumer which should care about IDs in this context.
  3. Potentially works if the user directly types in the browser console ng.watchSignal(someSignalFn), given they wouldn't be able to easily access signal IDs.

OTOH, I'm not convinced putting ReactiveNode in the ng global type is a great idea in the first place, given that we're trying to create a stronger abstraction not tied to a particular framework. However, signals are already converged across Angular and Wiz, and if you don't have signals (like ACX), then you could just omit this function entirely.

I'm not sure this is necessarily better than what you have, but just something to think about.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what I struggled with when originally prototyping the design you're suggesting was getting the signal getter function from the ReactiveNode to ensure the Watch or effect being created that logs the new value has a dependency on the signal and would run whenever it was updated. It appears I don't need to get the signal getter function since we can just call producerAccessed on the ReactiveNode. Do you have a preference? Happy to refactor if you feel strongly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel very strongly, up to you if this idea is 1. meaningfully better and 2. worth the effort to implement compared to what you have here.

Comment thread packages/core/src/render3/util/global_utils.ts Outdated
Comment thread packages/core/test/acceptance/signal_debug_spec.ts
csmick added 2 commits July 21, 2026 10:26
…ging

Implement \`toggleWatchSignal(id)\` in \`signal_debug.ts\` to enable toggling reactive watch listeners on individual signal graph nodes.
- Create reactive \`Watch\` instances using \`createWatch\` to log signal value/state changes to the console when active.
- Use \`WeakRef\` mapping and \`FinalizationRegistry\` (\`watchCleanupRegistry\`) so active watches do not retain strong references to signal nodes or prevent garbage collection.
- Expose \`watched\` status on \`DebugSignalGraphNode\` and publish \`toggleWatchSignal\` onto \`window.ng\` in development mode.
- Add acceptance tests covering watch activation, signal mutation logging, manual disposal, GC cleanup, and safe invalid ID handling.
Add a "Watch signal" button to the signal-details component to allow users to "watch" changes to the associated signal.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Issues related to the framework runtime area: devtools detected: feature PR contains a feature commit requires: TGP This PR requires a passing TGP before merging is allowed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Track Signal values by toggling their tracking in the devtools

3 participants