【问题标题】:how to wait for children load to complete when expanding a dynamically-loaded nested tree node扩展动态加载的嵌套树节点时如何等待子加载完成
【发布时间】:2021-10-05 08:46:21
【问题描述】:

我对 Angular 还是很陌生,所以我确定我只是在这里遗漏了一些相当简单的东西,但是我在任何地方都没有找到我正在做什么来回答我的问题的示例。

我正在使用服务成功地在 嵌套 角度材质树中动态加载分支。我通过定义一个“loadChildren”函数来实现这一点,该函数使服务调用填充子数组,在实例化时绑定到我的 NestedTreeControl 对象,基本上:

public nestedTreeControl: NestedTreeControl<Treenode>;
. . .
  constructor() {
    this.nestedTreeControl = new NestedTreeControl<Treenode>(this.loadChildren);

并用 ngIf 测试包装我的 ng-container,以便在完全加载之前不会实例化嵌套组件:

            <ul *ngIf="nestedTreeControl.isExpanded(tnode) && tnode.childrenLoaded">
                <ng-container matTreeNodeOutlet></ng-container>
            </ul>

这在我的 UI 中非常有效,可以动态扩展和加载子项。但是当我有一个已知“nodeId”值的有序数组(一个参数是上面 Treenode 对象的一部分)但列表中的 nodeIds 时,我正在尝试添加一个实用程序函数来递归扩展节点如果路径中的所有节点尚未在我的树中展开,则可能不会全部加载。

所以我有一个递归函数,类似于此处的 exp() 方法中显示的内容:https://stackblitz.com/edit/angular-icfxva?file=src%2Fapp%2Ftree-nested-overview-example.ts,这是我从这篇帖子的评论中找到的:(How to expand material nested tree to specific node?)。

如果我之前在我的 UI 中扩展了我的树以包含有序搜索数组中的所有 nodeId,那么我的函数效果很好。但是由于我的孩子在我展开节点时会动态加载,所以当我去展开一个未展开的节点(即nestedTreeControl.expand(tnode)(或在 stackblitz 示例中,treeControl.expand(node))时,我需要等待孩子们加载之前进行递归调用并传入子节点列表。

在进行下一次调用之前,我不确定如何“等待”孩子在我的递归函数中加载。什么/如何绑定到 NestedTreeControl 以确定子节点何时完成新扩展节点的加载(即,当我的节点的 'childrenLoaded' 标志最终设置为 'true' 时)?

【问题讨论】:

  • 我应该澄清...当我加载节点的第一(根)级时,结果包括一个适合根级节点的子对象数组,但每个子对象都有空 '儿童数组。因此,当一个节点展开时,'loadChildren' 会退出并从服务中加载每个展开的节点的子对象,这样我们就可以填充每个子节点的子数组。我仅限于通过服务结果来做到这一点,该服务结果用于其他应用程序。希望这是有道理的。

标签: angular angular-material tree nested


【解决方案1】:

经过一段时间的研究,我找到了答案。我最终添加了一个 EventEmitter 实例,一旦孩子完全加载,它就会在 loadChildren 方法中发出。

在我的递归函数中,我订阅新事件并等待递归调用自身,直到收到事件,但前提是尚未在我的节点对象上设置“childrenLoaded”标志,我会这样做如果节点未展开,则在展开节点之前立即检查/订阅。对于所有其他情况(以前加载的子级,无论是否扩展),我不订阅,而是立即进行递归调用。

此外,我没有在递归函数中传回最终找到的节点,而是发出第二个 EventEmitter,递归函数的原始第一个调用者正在订阅。

这似乎对于深度嵌套、动态加载的树非常有效。

【讨论】:

    【解决方案2】:

    原则上你可以这样做:

    async function expandToNode(node: TreeNode, p: (n: TreeNode) => boolean): Promise<void> {
      if (p(node)) {
        return;
      }
    
      await expandOneNode(node); // this will populate the `children` field asynchronously
    
      const promises = node.children!.map(r => expandToNode(r, p));
      await Promise.all(promises);
    }
    

    一些用于演示目的的虚拟结构:

    // This is just a dummy interface for demo purposes, yours is different.
    interface TreeNode {
      id: string;
      children?: TreeNode[];
      data?: any;
    }
    
    // Simulate async expansion of node that takes 1 second to complete
    async function expandOneNode(node: TreeNode): Promise<void> {
      return new Promise(resolve => {
        setTimeout(() => {
          node.children = []; // just an example
          resolve();
        }, 1000);
      });
    }
    

    用法:

    await expandToNode(root, n => n.data?.foundGold); // dummy criterion for demo purposes
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多