Angular 19: Enhanced Signal Debugging Features
Angular 19 introduces significant improvements to signal debugging capabilities. These improvements are primarily focused on enhancing the Angular DevTools experience.
Debug Names for Signals
All signal-related APIs now support a debugName
option that helps identify signals in debugging tools:
// Signal
const counter = signal(0, { debugName: 'counterSignal' });
// Computed
const doubled = computed(() => counter() * 2, { debugName: 'doubledSignal' });
// Effect
effect(() => console.log(counter()), { debugName: 'loggingEffect' });
// Input
@Component({...})
class MyComponent {
count = input(0, { debugName: 'countInput' });
}
// Model
@Component({...})
class MyComponent {
value = model(0, { debugName: 'valueModel' });
}
// Queries
@Component({...})
class MyComponent {
child = viewChild('childRef', { debugName: 'childQuery' });
children = viewChildren('itemRef', { debugName: 'itemsQuery' });
}
Signal Graph API
A new debug API getSignalGraph
has been introduced that provides visibility into the signal dependency graph:
interface DebugSignalGraphNode {
kind: string; // 'signal' | 'computed' | 'effect' | 'template' | 'unknown'
label: string; // The debugName or identifier
value?: unknown; // Current value (for signals and computed)
}
interface DebugSignalGraphEdge {
consumer: number; // Index of the consuming node
producer: number; // Index of the producing node
}
interface DebugSignalGraph {
nodes: DebugSignalGraphNode[];
edges: DebugSignalGraphEdge[];
}
Example usage:
@Component({
selector: 'my-component',
template: `<div>{{ computedValue() }}</div>`
})
class MyComponent {
private count = signal(0, { debugName: 'count' });
computedValue = computed(() => this.count() * 2, { debugName: 'doubledCount' });
constructor() {
// Get the signal graph for this component
const graph = getSignalGraph(this.injector);
// Returns nodes and edges representing the signal relationships
}
}
Type Identification for Signal Nodes
Angular 19 introduces a new kind
property to the signal prototype nodes, making it easier to identify different types of signals in debugging scenarios. This enhancement complements the existing debugging features by providing built-in type information for each signal node.
interface ReactiveNode {
// ... existing properties
kind: string; // Identifies the type of signal node
}
The kind
property can have various values including:
'signal'
- For basic signals'computed'
- For computed signals'effect'
- For effect signals'input'
- For input signals'unknown'
- Default value for base reactive nodes
Example usage with different signal types:
@Component({
selector: 'debug-component'
})
class DebugComponent {
// Basic signal shows 'signal' kind
counter = signal(0);
// Computed signal shows 'computed' kind
doubled = computed(() => this.counter() * 2);
// Effect shows 'effect' kind
counterEffect = effect(() => {
console.log('Counter:', this.counter());
});
constructor() {
// Using getSignalGraph to inspect node kinds
const graph = getSignalGraph(this.injector);
console.log(graph.nodes.map(node => node.kind));
}
}
Integration with Debug Tools The kind
property enhances debugging capabilities by:
Enabling precise filtering and grouping of signals by their type
Improving signal graph visualization with type-specific representations
Supporting better DevTools integration with type-aware debugging features
The design decision to use a string type instead of a strict union type ('signal' | 'computed' | 'effect'
) allows for future extensibility, enabling Angular and third-party libraries to define custom signal types without modifying the core type definitions.
When combined with the debugName
option and Signal Graph API, the kind
property provides a comprehensive debugging experience:
@Component({
selector: 'advanced-debug'
})
class AdvancedDebugComponent {
private count = signal(0, { debugName: 'counter' });
doubled = computed(() => this.count() * 2, { debugName: 'doubledCounter' });
constructor() {
const graph = getSignalGraph(this.injector);
// Now we can identify both the name and type of each signal
graph.nodes.forEach(node => {
console.log(`${node.debugName}: ${node.kind}`);
});
}
}
Future Enhancements
Automatic Debug Names: A TypeScript transform is being developed that will automatically add debug names to signals based on their usage context, eliminating the need for manual debug name specification.
Enhanced DevTools Integration: The Angular DevTools will leverage these new debugging features to provide:
Visual signal dependency graphs
Real-time signal value inspection
Signal relationship visualization
Performance monitoring for signal computations
Signal Graph Visualization: The DevTools will be able to create visual representations of signal relationships, making it easier to understand how data flows through your application.
Developer Benefits
Improved Debugging Experience:
Clear identification of signals in debugging tools
Better understanding of signal relationships
Easier tracking of signal value changes
Better Performance Analysis:
Ability to identify unnecessary signal computations
Understanding of signal dependency chains
Detection of potential optimization opportunities
Enhanced Development Workflow:
Quick identification of signal-related issues
Better understanding of reactive data flow
Simplified debugging of complex signal interactions
Implementation Notes
The signal debugging features are built on top of a new internal profiler system that tracks signal creation and relationships. This system is designed to have minimal performance impact in production builds while providing rich debugging information during development.
PRs you must follow
feat(core): introduce debugName optional arg to framework signal functions (#57073)
feat(core): implement getSignalGraph debug API (#57074)
feat(core): add "kind" to signal prototype nodes (#58333)
Thanks for reading so far 🙏
I’d like to have your feedback so please leave a comment, clap or follow. 👏
Spread the Angular love! 💜
If you really liked it, share it among your community, tech bros and whoever you want! 🚀👥
Don't forget to follow me and stay updated: 📱
Thanks for being part of this Angular journey! 👋😁