ngOnChanges 只击中树的根,而不是所有的后代

ngOnChanges only hitting root of tree, not all offsprings

我的 parent 组件有一个非二叉树节点(树的根)作为 child。 每个节点可以有0个或多个children类型的节点,children可以折叠或展开。 此外,在我的 parent 组件中,有一个按钮可以让您 Collapse AllExpand All children of树中的所有节点。

我有一个枚举标记整棵树是折叠的(通过按钮),整棵树是展开的(通过按钮),还是树的状态是手动控制的(这与这个问题无关)。

枚举如下所示:

export enum TreeStatus{
  Collapsed,
  Expanded,
  Manual
}

parent.component.ts:

export class ParentComponent implements OnInit {
  root: Node;
  public treeStatus= TreeStatus.Collapsed;   //in the beginning, every node in the tree has children collapsed
  public isAnyNodeChildrenCollapsed = true;  //checks if any node has children collapsed(mainly used for manual toggle)

  onExpandAll() {
    this.treeStatus= TreeStatus.Expanded;
    this.isAnyNodeChildrenCollapsed = false;
  }

  onCollapseAll(){
    this.treeStatus= TreeStatus.Collapsed;
    this.isAnyNodeChildrenCollapsed = true;
  }
  
  ...
}

parent.component.html:

<div>
  <button *ngIf="!isAnyNodeChildrenCollapsed " (click)="onCollapseAll()">  //if all nodes have children expanded, we will see the button that allows us to collapse all
    Collapse All
  </button>
  <button *ngIf="isAnyNodeChildrenCollapsed " (click)="onExpandAll()">     //if at least one node has children collapsed, we will see the button that allows us to expand all
    Expand All
  </button>
</div>
<div>
  <tree-item [node]="root" [treeStatus]="treeStatus"></tree-item>            //the root of my tree
</div>

tree-item.component.ts:

export class TreeItemComponent implements OnInit, OnChanges{
   @Input() node: Node;
   @Input() treeStatus: TreeStatus;
   public isChildrenCollapsed = true;  //initially, all nodes have children collapsed

   ngOnChanges(changes: SimpleChanges) {
    console.log(this.node.id + ": " + changes.treeStatus.previousValue + " => " + changes.TreeStatus.currentValue);   
    //this log above best displays the issue. When I click on Expand All, all nodes in my tree appear in the logs, but when I click Collapse All, even though this is the same callback method, only the root appears to detect the change.(even so, the toggle somehow happens regardless)

    if (changes.treeStatus && changes.treeStatus.currentValue!=NavigatorTreeStatus.Manual) {
      this.isChildrenCollapsed = !(changes.treeStatus.currentValue == NavigatorTreeStatus.Expanded);
      }
    }
  }

}

tree-item.component.html(基本DFS树解析):

<div>
//display node here
</div>
<div *ngIf="node.children.length > 0 && !isChildrenCollapsed">
  <tree-item *ngFor="let child of node.children"
             [node]="child" 
             [treeStatus]="treeStatus" 
  >
</div>

我根本不明白为什么,当我点击 Expand All,从而将 treeStatus 更改为 Expanded 时,ngOnChanged 正常工作并按预期传播到三个节点中的每个节点,但是当点击 Collapse All 现在触发相同的 ngOnChanges 回调方法时,唯一检测到变化的节点似乎是根节点。

日志显示如下: 最初,当打开组件时,root id 获得状态 0(Collapsed),而不是 undefined:

2013777645: undefined => 0

点击全部展开后,root Id 的值从 0(Collapsed) 变为 1(Expanded),其余节点从 undefined 变为 1(Expanded) )

2013777645: 0 => 1

2118369458: undefined => 1

368241412: undefined => 1

1860836078: undefined => 1

1979885541: undefined => 1

1316570423: undefined => 1

360484732: undefined => 1

506271756: undefined => 1

现在,点击 全部折叠 后,只有根出现在日志中,值从 1(展开)更改为 0(折叠)。

2013777645: 1 => 0

最后,如果我们现在再次点击 Expand All,将出现与之前完全相同的日志,即使其他节点不应该具有未定义的 treeStatus 值,因为它已经之前被赋值了。

2013777645: 0 => 1

2118369458: undefined => 1

368241412: undefined => 1

1860836078: undefined => 1

1979885541: undefined => 1

1316570423: undefined => 1

360484732: undefined => 1

506271756: undefined => 1

你能帮我理解我错过了什么吗? 我真的希望你能从这个解释中理解这个问题。 提前致谢!

您似乎使用 ngIf 隐藏了子节点。当 ngIf 为 false 时,您的组件(或模板)不仅被隐藏,而且被销毁。您可以找到更多详细信息 here.