You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular递归Tree组件如何实现nodeSelected事件仅触发一次?

Hey there! Let's break down why you're seeing multiple event triggers and fix it step by step.

The Root Cause

Your current setup creates a chain of unnecessary event forwarding:

  1. When you click node C, its treeNodeSelected method runs, logs "node" and emits the event.
  2. The parent Tree component (for node B) has (nodeSelected)="treeNodeSelected($event)", so it calls its own treeNodeSelected—logging again and emitting the event.
  3. This repeats for the grandparent Tree component (node A), leading to 3 "node" logs before the event finally reaches your root actual-component.

Simple Fix: Forward Child Events Directly (No Extra Logging)

Instead of passing child component events through your treeNodeSelected method (which adds duplicate logs), forward them directly to the parent using nodeSelected.emit($event) in the template. Here's how to update your code:

1. Update the Tree Component's HTML

Remove the call to treeNodeSelected for child tree components, and emit the event directly:

<ul>
  <li *ngFor="let item of node">
    <div>
      <span (click)="treeNodeSelected(item)">{{item.name}}</span>
    </div>
    <!-- Forward child nodeSelected events directly without extra processing -->
    <tree *ngIf="item.child.length > 0" 
          [node]="item.child" 
          (nodeSelected)="nodeSelected.emit($event)">
    </tree>
  </li>
</ul>

2. Clean Up the Tree Component's TS

Your treeNodeSelected method should only handle clicks on the current component's nodes. You can also remove the true from EventEmitter (it enables async mode, which isn't needed here):

@Input("node") node;
@Output() nodeSelected = new EventEmitter<any>();

treeNodeSelected(node) {
  console.log("node", node);
  this.nodeSelected.emit(node);
}

How This Works

  • When you click a child node (like C), its treeNodeSelected runs once (logging "node" and emitting the event).
  • Parent components (B and A) simply forward the event up the chain without running their own treeNodeSelected—so no duplicate logs.
  • Finally, your actual-component receives the event exactly once, logging "End Component".

Alternative: Use ViewChildren for Programmatic Forwarding

If you prefer to handle event forwarding in the component class instead of the template, you can use ViewChildren to subscribe to child component events:

Tree Component TS:

import { Component, Input, Output, EventEmitter, ViewChildren, QueryList, AfterViewInit } from '@angular/core';
import { TreeComponent } from './tree.component'; // Import your Tree component

@Component({
  selector: 'tree',
  templateUrl: './tree.component.html'
})
export class TreeComponent implements AfterViewInit {
  @Input("node") node;
  @Output() nodeSelected = new EventEmitter<any>();

  @ViewChildren(TreeComponent) childTrees: QueryList<TreeComponent>;

  ngAfterViewInit() {
    // Subscribe to each child's nodeSelected event and forward it
    this.childTrees.forEach(childTree => {
      childTree.nodeSelected.subscribe(node => {
        this.nodeSelected.emit(node);
      });
    });
  }

  treeNodeSelected(node) {
    console.log("node", node);
    this.nodeSelected.emit(node);
  }
}

Tree Component HTML (remove the (nodeSelected) binding):

<ul>
  <li *ngFor="let item of node">
    <div>
      <span (click)="treeNodeSelected(item)">{{item.name}}</span>
    </div>
    <tree *ngIf="item.child.length > 0" [node]="item.child"></tree>
  </li>
</ul>

This achieves the same result but keeps your template cleaner if you have more complex logic to handle later.

Either approach will stop the duplicate event triggers and logs while ensuring the event reaches your root component correctly.

内容的提问来源于stack exchange,提问作者Code Guru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:34:06