Repository: angular/angular
File: devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.ts (TypeScript)

Code before:
 */

import {ChangeDetectionStrategy, Component, computed, input, output} from '@angular/core';
import {MatIconModule} from '@angular/material/icon';
import {MatTooltipModule} from '@angular/material/tooltip';
import {MatTreeModule} from '@angular/material/tree';

interface TreeNode {
  name: string;
  value: any;
  preview: string;
  isExpandable: boolean;
  children: TreeNode[];
}

@Component({
  selector: 'ng-route-data-tree',
  templateUrl: './route-data-tree.component.html',
  styleUrls: ['./route-data-tree.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [MatTreeModule, MatIconModule, MatTooltipModule],
})
export class RouteDataTreeComponent {
  readonly data = input.required<any>();

  readonly showButton = input(false);

  readonly viewSource = output<string>();

  protected readonly treeData = computed(() => this.buildTree(this.data()));

  protected readonly childrenAccessor = (node: TreeNode): TreeNode[] => node.children;

  private buildTree(data: any): TreeNode[] {
    const isArray = Array.isArray(data);
    const hasKeyValue = isArray && data?.[0]?.key !== undefined;

    if (isArray && hasKeyValue) {
      const obj: Record<string, any> = {};
      data.forEach((item: any) => {
        obj[item.key] = item.value;
      });
      return this.buildObjectNodes(obj);
    }

    return isArray ? this.buildArrayNodes(data) : this.buildObjectNodes(data);
  }

  private isObject(value: any): boolean {
    return value !== null && typeof value === 'object';
  }

Review comment (suggestion):
Nit:
```suggestion
  readonly showViewSourceButton = input(false);
```

Code after:
 */

import {ChangeDetectionStrategy, Component, computed, input, output} from '@angular/core';
import {MatIconModule} from '@angular/material/icon';
import {MatTooltipModule} from '@angular/material/tooltip';
import {MatTreeModule} from '@angular/material/tree';

interface TreeNode {
  name: string;
  value: any;
  preview: string;
  isExpandable: boolean;
  children: TreeNode[];
}

@Component({
  selector: 'ng-route-data-tree',
  templateUrl: './route-data-tree.component.html',
  styleUrls: ['./route-data-tree.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [MatTreeModule, MatIconModule, MatTooltipModule],
})
export class RouteDataTreeComponent {
  readonly data = input.required<any>();

  readonly showViewSourceButton = input(false);

  readonly viewSource = output<string>();

  protected readonly treeData = computed(() => this.buildTree(this.data()));

  protected readonly childrenAccessor = (node: TreeNode): TreeNode[] => node.children;

  private buildTree(data: any): TreeNode[] {
    const isArray = Array.isArray(data);
    const hasKeyValue = isArray && data?.[0]?.key !== undefined;

    if (isArray && hasKeyValue) {
      const obj: Record<string, any> = {};

      for (const item of data) {
        obj[item.key] = item.value;
      }

      return this.buildObjectNodes(obj);
    }

    return isArray ? this.buildArrayNodes(data) : this.buildObjectNodes(data);
  }

  private isObject(value: any): boolean {