Hone logo
Hone
Problems

Crafting an Angular Debugging Inspector

This challenge focuses on building a custom debugging tool within an Angular application. You'll create a component that can inspect the state of other components, providing insights into their properties and methods in real-time, mimicking some functionalities of browser developer tools. This is crucial for understanding complex component interactions and troubleshooting issues efficiently.

Problem Description

Your task is to develop a reusable Angular component, DebugInspectorComponent, that allows developers to select a target component within the application and view its internal state. The inspector should dynamically display the public properties and methods of the selected component.

Key Requirements:

  1. Component Selection: The inspector needs a mechanism to select a target component from a list of available components in the current view. This could be achieved via a dropdown or a clickable list.
  2. Property Inspection: Display the names and current values of all public properties of the selected component. Changes to these properties should be reflected in the inspector.
  3. Method Listing: List the names of all public methods of the selected component.
  4. Method Invocation (Optional but Recommended): Allow the user to trigger a method call on the target component from the inspector. For simplicity, assume methods don't require complex arguments for this challenge, or they accept a predefined simple type.
  5. Dynamic Updates: The inspector should update its displayed information whenever the target component's state changes.
  6. Error Handling: Gracefully handle cases where a component might not be selectable or might not have any public properties/methods.

Expected Behavior:

When the DebugInspectorComponent is rendered, it should:

  • Present a way to choose a component (e.g., a list of component names).
  • Upon selection of a component, it should fetch and display its public properties (name and value).
  • It should also display the names of its public methods.
  • If a method invocation feature is implemented, it should provide a way to call a selected method.
  • As the selected component's properties change (e.g., through user interaction or data binding), the inspector should update its display to show the latest values.

Edge Cases:

  • Components with no public properties or methods.
  • Components with properties of complex types (e.g., nested objects, arrays). For this challenge, focus on displaying primitive types and simple object representations.
  • Components that are dynamically added or removed from the DOM.
  • Circular dependencies or self-referencing properties.

Examples

Let's assume we have a simple UserProfileComponent in our application:

// user-profile.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-user-profile',
  template: `
    <h2>{{ name }}</h2>
    <p>Email: {{ email }}</p>
    <button (click)="changeName()">Change Name</button>
  `,
})
export class UserProfileComponent {
  @Input() userId: number = 1;
  name: string = 'John Doe';
  email: string = 'john.doe@example.com';
  isActive: boolean = true;
  settings: { theme: string, notifications: boolean } = { theme: 'dark', notifications: true };

  changeName() {
    this.name = 'Jane Smith';
  }

  resetProfile() {
    console.log('Profile reset initiated.');
    this.name = 'John Doe';
    this.email = 'john.doe@example.com';
  }
}

Example 1:

Input: The DebugInspectorComponent is initialized, and UserProfileComponent is present in the application view. The user selects UserProfileComponent from the inspector's component selection interface.

Output (within the DebugInspectorComponent):

Selected Component: UserProfileComponent

Properties:
  userId: 1
  name: John Doe
  email: john.doe@example.com
  isActive: true
  settings: { theme: 'dark', notifications: true }

Methods:
  changeName
  resetProfile

Explanation: The inspector correctly identifies and lists the public properties (userId, name, email, isActive, settings) and their current values, along with the public methods (changeName, resetProfile) of the UserProfileComponent.

Example 2:

Input: Following Example 1, the user clicks the "Change Name" button within the UserProfileComponent's template.

Output (within the DebugInspectorComponent):

Selected Component: UserProfileComponent

Properties:
  userId: 1
  name: Jane Smith  // <-- Updated
  email: john.doe@example.com
  isActive: true
  settings: { theme: 'dark', notifications: true }

Methods:
  changeName
  resetProfile

Explanation: The DebugInspectorComponent automatically detects the change in the name property of UserProfileComponent and updates its display to show "Jane Smith".

Example 3 (Method Invocation - if implemented):

Input: Following Example 2, the user types "resetProfile" into a method invocation input field within the DebugInspectorComponent and clicks an "Invoke" button.

Output (within the DebugInspectorComponent and Console):

Selected Component: UserProfileComponent

Properties:
  userId: 1
  name: John Doe   // <-- Updated after method call
  email: john.doe@example.com // <-- Updated after method call
  isActive: true
  settings: { theme: 'dark', notifications: true }

Methods:
  changeName
  resetProfile

Console Output:

Profile reset initiated.

Explanation: The DebugInspectorComponent successfully invoked the resetProfile method on the UserProfileComponent. The UserProfileComponent's state was updated (name and email reset), and the inspector reflected these changes.

Constraints

  • The DebugInspectorComponent should be designed as a standalone component or a module that can be easily imported and used in any Angular application.
  • The solution must use TypeScript.
  • The inspector should only display public properties and methods. Private members should be ignored.
  • Performance: The component selection and property/method listing should be reasonably fast. Avoid excessive DOM manipulation or computationally expensive operations during rendering.
  • For method invocation, assume methods take zero or one argument of a primitive type (string, number, boolean) or undefined.

Notes

  • Consider how to obtain a list of all components currently rendered in the application's view. Angular's ChangeDetectorRef or other dependency injection mechanisms might be useful here.
  • Reflecting on component instances can be done using Object.keys() and checking for typeof or by inspecting class decorators if necessary, but focus on readily accessible public members first.
  • For dynamic updates, consider using Angular's change detection mechanism or RxJS subjects to subscribe to property changes.
  • Think about how to represent complex data types (objects, arrays) in a readable way within the inspector. A simple JSON stringification might suffice for basic cases.
  • The primary goal is to demonstrate an understanding of introspection and dynamic UI generation within Angular.
Loading editor...
typescript