Skip to content

fix(overlay): walkAngularTree() drops nested child components inside HTML wrapper elements #11

Closed

Description

@Kaap10

Summary

In packages/ng-devtools/src/overlay.ts, walkAngularTree() traverses the DOM to build the live component hierarchy. However, the traversal immediately exits (if (!component) return;) whenever an element is not an Angular component host.

Because standard Angular templates wrap nested child components inside common HTML layout elements (such as <div class="container">, <main>, <section>, or <ul>), ng.getComponent() on those wrapper elements returns null. This causes walkAngularTree() to abort recursion prematurely, dropping all nested child components from the DevTools Component Tree and the MCP ng-devtools:component-tree agent resource.


Code Reference

In packages/ng-devtools/src/overlay.ts (lines 108–125):

function walkAngularTree(el: Element, out: ComponentTreeNode[], ng: any) {
  const component = ng.getComponent(el);
  if (!component) return; // Aborts traversal if element is not a component

  const node: ComponentTreeNode = {
    id: generateId(el),
    selector: el.tagName.toLowerCase(),
    tagName: el.tagName.toLowerCase(),
    children: [],
    inputs: tryGetInputs(component),
  };

  for (const child of el.querySelectorAll(':scope > *')) {
    walkAngularTree(child, node.children, ng);
  }

  out.push(node);
}

Reproduction & Failure Scenario

Given an Angular component template with standard HTML wrappers:

<app-root>
  <div class="app-layout">
    <app-header></app-header>
    <main>
      <app-product-list></app-product-list>
    </main>
  </div>
</app-root>
  1. walkAngularTree starts at <app-root> and inspects direct child <div class="app-layout">.
  2. ng.getComponent(div) returns null (since <div> is a native DOM element, not a component host).
  3. if (!component) return; fires immediately.
  4. Neither <app-header> nor <app-product-list> are ever reached or added to node.children.
  5. The resulting Component Tree contains only <app-root>.

Proposed Solution

Modify walkAngularTree so that if an element is not a component host, it continues recursing through its child DOM nodes and attaches any discovered child components to the current component node's children array:

function walkAngularTree(el: Element, out: ComponentTreeNode[], ng: any) {
  const component = ng.getComponent(el);

  if (component) {
    const node: ComponentTreeNode = {
      id: generateId(el),
      selector: el.tagName.toLowerCase(),
      tagName: el.tagName.toLowerCase(),
      children: [],
      inputs: tryGetInputs(component),
    };

    for (const child of el.children) {
      walkAngularTree(child, node.children, ng);
    }

    out.push(node);
  } else {
    // Continue traversing through non-component DOM wrapper nodes
    for (const child of el.children) {
      walkAngularTree(child, out, ng);
    }
  }
}

Affected Files

  • packages/ng-devtools/src/overlay.ts

Activity

  1. Kaap10 commented on Sep 23, 2026

    @Kaap10
    ContributorAuthor

    Hi @santoshyadavdev, while testing the live Component Tree on a sample app with nested layouts, I noticed that child components inside wrapper tags (<div>, <section>, etc.) were not showing up in the tree. Traced it back to walkAngularTree() early-returning on non-component DOM elements.

    I have identified the root cause and would love to submit a PR!

  2. santoshyadavdev commented on Sep 23, 2026

    @santoshyadavdev
    Collaborator

    Go for it, we need as many hands as possible, let's do it 💙

  3. Kaap10 commented on Sep 24, 2026

    @Kaap10
    ContributorAuthor

    Submitted the fix and unit tests in PR #12, ready for review! @santoshyadavdev @erkamyaman

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions