refactor(devtools): Use OnPush on all components (#62610)

The end goal is to move the devtools app to zoneless.

PR Close #62610
This commit is contained in:
Matthieu Riegler
2025-07-12 21:01:49 +02:00
committed by Andrew Kushnir
parent a5b45ca2f6
commit 5aa69ff0a2
25 changed files with 94 additions and 22 deletions
@@ -6,7 +6,15 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, inject, input, output, signal} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
output,
signal,
} from '@angular/core';
import {MatIcon} from '@angular/material/icon';
import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu';
import {MatSlideToggle} from '@angular/material/slide-toggle';
@@ -54,6 +62,7 @@ type Tab = 'Components' | 'Profiler' | 'Router Tree' | 'Injector Tree';
MatSlideToggle,
],
providers: [TabUpdate],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DevToolsTabsComponent {
readonly isHydrationEnabled = input(false);
@@ -16,6 +16,7 @@ import {
output,
signal,
viewChild,
ChangeDetectionStrategy,
} from '@angular/core';
import {
ComponentExplorerView,
@@ -98,6 +99,7 @@ const sameDirectives = (a: IndexedNode, b: IndexedNode) => {
SignalsTabComponent,
ResponsiveSplitDirective,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DirectiveExplorerComponent {
readonly showCommentNodes = input(false);
@@ -7,6 +7,7 @@
*/
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
@@ -25,6 +26,7 @@ import {MatIcon} from '@angular/material/icon';
templateUrl: './breadcrumbs.component.html',
styleUrls: ['./breadcrumbs.component.scss'],
imports: [MatIcon],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BreadcrumbsComponent {
readonly parents = input.required<FlatNode[]>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, input, output} from '@angular/core';
import {ChangeDetectionStrategy, Component, input, output} from '@angular/core';
import {MatIcon} from '@angular/material/icon';
export type FilterMatch = {
@@ -44,6 +44,7 @@ const genericSearchGenerator: FilterFnGenerator = (filter: string) => {
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.scss'],
imports: [MatIcon],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FilterComponent {
readonly filter = output<FilterFn>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, input} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, input} from '@angular/core';
import {MatToolbar} from '@angular/material/toolbar';
import {DeferInfo} from '../../../../../../../protocol';
@@ -15,6 +15,7 @@ import {DeferInfo} from '../../../../../../../protocol';
selector: 'ng-defer-view',
styleUrls: ['./defer-view.component.scss'],
imports: [MatToolbar],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DeferViewComponent {
readonly defer = input.required<NonNullable<DeferInfo>>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, input, output} from '@angular/core';
import {ChangeDetectionStrategy, Component, input, output} from '@angular/core';
import {DirectivePosition} from '../../../../../../protocol';
import {IndexedNode} from '../directive-forest/index-forest';
@@ -20,6 +20,7 @@ import {DeferViewComponent} from './defer-view/defer-view.component';
templateUrl: './property-tab.component.html',
styleUrls: ['./property-tab.component.scss'],
imports: [PropertyTabHeaderComponent, PropertyTabBodyComponent, DeferViewComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PropertyTabComponent {
readonly currentSelectedElement = input.required<IndexedNode | null>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, input} from '@angular/core';
import {ChangeDetectionStrategy, Component, input} from '@angular/core';
import {SerializedInjectedService} from '../../../../../../../protocol';
import {ResolutionPathComponent} from './resolution-path/resolution-path.component';
import {MatTooltip} from '@angular/material/tooltip';
@@ -17,6 +17,7 @@ import {MatExpansionModule} from '@angular/material/expansion';
templateUrl: './dependency-viewer.component.html',
styleUrl: './dependency-viewer.component.scss',
imports: [MatExpansionModule, MatTooltip, ResolutionPathComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DependencyViewerComponent {
readonly dependency = input.required<SerializedInjectedService>();
@@ -15,6 +15,7 @@ import {
output,
signal,
viewChild,
ChangeDetectionStrategy,
} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {ContainerType} from '../../../../../../../protocol';
@@ -40,6 +41,7 @@ const parseValue = (value: EditorResult): EditorResult => {
selector: 'ng-property-editor',
styleUrls: ['./property-editor.component.scss'],
imports: [FormsModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PropertyEditorComponent {
readonly key = input.required<string>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, input, output} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, input, output} from '@angular/core';
import {PropType} from '../../../../../../../protocol';
import {FlatNode} from '../../property-resolver/element-property-resolver';
@@ -15,6 +15,7 @@ import {FlatNode} from '../../property-resolver/element-property-resolver';
selector: 'ng-property-preview',
templateUrl: './property-preview.component.html',
styleUrls: ['./property-preview.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PropertyPreviewComponent {
readonly node = input.required<FlatNode>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, input, output} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, input, output} from '@angular/core';
import {DirectivePosition} from '../../../../../../../protocol';
import {IndexedNode} from '../../directive-forest/index-forest';
@@ -18,6 +18,7 @@ import {PropertyViewComponent} from './property-view.component';
selector: 'ng-property-tab-body',
styleUrls: ['./property-tab-body.component.scss'],
imports: [PropertyViewComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PropertyTabBodyComponent {
readonly currentSelectedElement = input.required<IndexedNode>();
@@ -8,6 +8,7 @@
import {CdkDragDrop, moveItemInArray, CdkDropList, CdkDrag} from '@angular/cdk/drag-drop';
import {
ChangeDetectionStrategy,
Component,
ɵFramework as Framework,
computed,
@@ -42,6 +43,7 @@ import {DependencyViewerComponent} from './dependency-viewer.component';
CdkDrag,
PropertyViewTreeComponent,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PropertyViewBodyComponent {
readonly controller = input.required<DirectivePropertyResolver>();
@@ -134,6 +136,7 @@ export class PropertyViewBodyComponent {
`,
],
imports: [DependencyViewerComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InjectedServicesComponent {
readonly controller = input.required<DirectivePropertyResolver>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, inject, input, output} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, inject, input, output} from '@angular/core';
import {MatIcon} from '@angular/material/icon';
import {MatTooltip} from '@angular/material/tooltip';
import {MatToolbar} from '@angular/material/toolbar';
@@ -18,6 +18,7 @@ import {FrameManager} from '../../../../application-services/frame_manager';
templateUrl: './property-view-header.component.html',
styleUrls: ['./property-view-header.component.scss'],
imports: [MatToolbar, MatTooltip, MatIcon],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PropertyViewHeaderComponent {
readonly directive = input.required<string>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, inject, input, output} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, inject, input, output} from '@angular/core';
import {DirectivePosition} from '../../../../../../../protocol';
import {ElementPropertyResolver, FlatNode} from '../../property-resolver/element-property-resolver';
@@ -18,6 +18,7 @@ import {PropertyViewHeaderComponent} from './property-view-header.component';
templateUrl: './property-view.component.html',
styleUrls: ['./property-view.component.scss'],
imports: [PropertyViewHeaderComponent, PropertyViewBodyComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PropertyViewComponent {
readonly directive = input.required<{name: string}>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, input} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, input} from '@angular/core';
import {SerializedInjector} from '../../../../../../../../protocol';
export const NODE_TYPE_CLASS_MAP: {[key in SerializedInjector['type']]: string} = {
@@ -21,6 +21,7 @@ export const NODE_TYPE_CLASS_MAP: {[key in SerializedInjector['type']]: string}
selector: 'ng-resolution-path',
templateUrl: './resolution-path.component.html',
styleUrl: './resolution-path.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ResolutionPathComponent {
readonly path = input<SerializedInjector[]>([]);
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, input} from '@angular/core';
import {ChangeDetectionStrategy, Component, input} from '@angular/core';
import {FlatTreeControl} from '@angular/cdk/tree';
import {MatTree, MatTreeNode, MatTreeNodeDef, MatTreeNodePadding} from '@angular/material/tree';
import {MatIcon} from '@angular/material/icon';
@@ -30,6 +30,7 @@ export interface Property {
templateUrl: './signals-value-tree.component.html',
imports: [MatTree, MatTreeNode, MatTreeNodeDef, MatTreeNodePadding, MatIcon],
styleUrl: './signals-value-tree.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SignalsValueTreeComponent {
readonly treeControl = input.required<FlatTreeControl<FlatNode>>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, inject, input, signal} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, inject, input, signal} from '@angular/core';
import {MatIcon} from '@angular/material/icon';
import {MatTableModule} from '@angular/material/table';
import {MatTooltip} from '@angular/material/tooltip';
@@ -23,6 +23,7 @@ import {ButtonComponent} from '../../../shared/button/button.component';
templateUrl: './injector-providers.component.html',
styleUrl: './injector-providers.component.scss',
imports: [MatTableModule, MatIcon, MatTooltip, ButtonComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InjectorProvidersComponent {
readonly injector = input.required<SerializedInjector>();
@@ -8,6 +8,7 @@
import {
afterRenderEffect,
ChangeDetectionStrategy,
Component,
computed,
ElementRef,
@@ -73,6 +74,7 @@ const HIERARCHY_HOR_SIZE = 50;
host: {
'[hidden]': 'hidden()',
},
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InjectorTreeComponent {
private readonly environmentTree = viewChild<TreeVisualizerHostComponent>('environmentTree');
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, inject, signal} from '@angular/core';
import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core';
import {MatDialog} from '@angular/material/dialog';
import {MatIcon} from '@angular/material/icon';
import {MatTooltip} from '@angular/material/tooltip';
@@ -28,6 +28,7 @@ const PROFILER_VERSION = 1;
templateUrl: './profiler.component.html',
styleUrls: ['./profiler.component.scss'],
imports: [MatTooltip, MatIcon, RecordingTimelineComponent, ButtonComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProfilerComponent {
readonly state = signal<State>('idle');
@@ -13,6 +13,7 @@ import {
} from '@angular/cdk/scrolling';
import {
afterRenderEffect,
ChangeDetectionStrategy,
Component,
computed,
ElementRef,
@@ -70,6 +71,7 @@ function framesBoundSignal<T>(source: Signal<ProfilerFrame[]>, defaultValue: T)
ButtonComponent,
DecimalPipe,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FrameSelectorComponent {
private readonly tabUpdate = inject(TabUpdate);
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component} from '@angular/core';
import {Component, ChangeDetectionStrategy} from '@angular/core';
import {MatProgressBar} from '@angular/material/progress-bar';
@Component({
@@ -14,5 +14,6 @@ import {MatProgressBar} from '@angular/material/progress-bar';
templateUrl: './recording-dialog.component.html',
styleUrls: ['./recording-dialog.component.scss'],
imports: [MatProgressBar],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RecordingDialogComponent {}
@@ -6,7 +6,16 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, effect, input, linkedSignal, output, signal} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
input,
linkedSignal,
output,
signal,
} from '@angular/core';
import {ProfilerFrame} from '../../../../../../protocol';
import {Observable} from 'rxjs';
@@ -32,6 +41,7 @@ import {estimateFrameRate} from './shared/estimate-frame-rate';
RecordingVisualizerComponent,
VisualizerControlsComponent,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RecordingTimelineComponent {
readonly stream = input.required<Observable<ProfilerFrame[]>>();
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, input, output} from '@angular/core';
import {Component, computed, input, output, ChangeDetectionStrategy} from '@angular/core';
import {ButtonComponent} from '../../shared/button/button.component';
export type RowType = 'text' | 'chip' | 'flag' | 'list';
@@ -16,6 +16,7 @@ export type RowType = 'text' | 'chip' | 'flag' | 'list';
templateUrl: './route-details-row.component.html',
styleUrls: ['./route-details-row.component.scss'],
imports: [ButtonComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RouteDetailsRowComponent {
readonly label = input.required<string>();
@@ -6,8 +6,17 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {CommonModule} from '@angular/common';
import {afterNextRender, Component, effect, inject, input, signal, viewChild} from '@angular/core';
import {JsonPipe} from '@angular/common';
import {
afterNextRender,
ChangeDetectionStrategy,
Component,
effect,
inject,
input,
signal,
viewChild,
} from '@angular/core';
import {TreeVisualizerHostComponent} from '../../shared/tree-visualizer-host/tree-visualizer-host.component';
import {MatIconModule} from '@angular/material/icon';
import {ApplicationOperations} from '../../application-operations/index';
@@ -33,7 +42,7 @@ const DEFAULT_FILTER = /.^/;
templateUrl: './router-tree.component.html',
styleUrls: ['./router-tree.component.scss'],
imports: [
CommonModule,
JsonPipe,
TreeVisualizerHostComponent,
SplitComponent,
SplitAreaDirective,
@@ -41,7 +50,7 @@ const DEFAULT_FILTER = /.^/;
RouteDetailsRowComponent,
ButtonComponent,
],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RouterTreeComponent {
private routerTree = viewChild.required<TreeVisualizerHostComponent>('routerTree');
@@ -7,7 +7,15 @@
*/
import {animate, style, transition, trigger} from '@angular/animations';
import {Component, computed, inject, OnDestroy, OnInit, signal} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
OnDestroy,
OnInit,
signal,
} from '@angular/core';
import {Events, MessageBus, SupportedApis} from '../../../protocol';
import {interval} from 'rxjs';
@@ -55,6 +63,7 @@ const LAST_SUPPORTED_VERSION = 9;
],
imports: [DevToolsTabsComponent, MatTooltip, MatProgressSpinnerModule, MatTooltipModule],
providers: [WINDOW_PROVIDER, ThemeService],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DevToolsComponent implements OnDestroy {
readonly AngularStatus = AngularStatus;
@@ -6,7 +6,14 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, ElementRef, input, signal, viewChild} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
input,
signal,
viewChild,
} from '@angular/core';
let instanceIdx = 0;
@@ -25,6 +32,7 @@ let instanceIdx = 0;
</svg>
`,
styleUrl: 'tree-visualizer-host.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TreeVisualizerHostComponent {
readonly container = viewChild.required<ElementRef>('container');