【问题标题】:Create tree in angular-grid (ag-grid) with async data loading使用异步数据加载在角度网格(ag-grid)中创建树
【发布时间】:2017-02-16 20:02:04
【问题描述】:

我正在尝试使用 angular-grid (ag-grid) 来显示类似于文档中提供的示例中的树:

http://www.angulargrid.com/example-file-browser/index.php

在给定的示例中,所有数据都已提供。展开行组时如何使用异步数据加载?我的猜测是我需要编写自己的组行渲染器。

【问题讨论】:

    标签: angularjs angular-grid


    【解决方案1】:

    我最近在我的 React.js 应用程序中遇到了同样的问题并找到了解决方案。它类似于@leden 发布的内容,但我找到了如何在表行更新之间维护当前行扩展的解决方案。

    解决方法如下:

    1. 为每个顶级行添加虚拟子行。可以为空,也可以加载...字符串,例如在第一列中。

    2. 在每次更新表 rowData 时调用的事件 getNodeChildDetails 上,您可以指定是否应该扩展行。因此,我们的想法是,我们会跟踪扩展的内容和未扩展的内容。

      getNodeChildDetails = (rowItem) => {
        if (rowItem.children) {
          return {
            group: true,
            expanded: rowItem.id in this.expandedRows,
            children: rowItem.children,
          };
        }
        else {
          return null;
        }
      };
      
    3. 在事件 rowGroupOpened 上,我们会跟踪展开了哪些行。

      rowGroupOpened = (param) => {
        const id= param.node.data.id;
      
        if(!param.node.expanded) {
          delete this.expandedRows[id];
          return;
        }
      
        this.expandedRows[id] = true;
      
        if (param.node.data.children.length !== 1) { // Here we need to check if only dummy row is present
            return;
          }
      
          this.api.showLoadingOverlay();
      
          // Here I simulate fetching data from server
          setTimeout(() => {
            this.rowData.forEach((e) => {
              if (e.id == id) {
                e.children = [
                  // Add  fetch rows
                ]
              }
            });
      
            this.api.setRowData(this.rowData); // Setting data, will trigger getNodeChildDetails call on each row
            this.api.hideOverlay();
          }, 1000);
        };
      

    【讨论】:

    • this.expandedRows 是什么对象?
    • 它可以是任何将 id 映射为布尔值(对象、地图、字典)的集合。
    【解决方案2】:

    网格不支持开箱即用的延迟加载树数据。所以是的,你必须编写自己的 cellRenderer 来实现这一点。

    PS 我是 ag-Grid 的作者,所以你可以把这个答案当作福音!

    【讨论】:

    • 嗨 Niall,如果我的要求不高,您能详细说明一下吗?由于编写自己的 groupRowRenderer 会丢弃这么多标准和有用的行为(扩展、折叠和所有这些),我在想是否有一种侵入性较小的方法。例如,是否可以在 RowExpand 上进行 ajax 调用?或者节点没有子节点(在单击/尝试扩展时)不允许调用“onRowExpand”这一事实?
    • 另外,这不会影响虚拟化功能吗?
    • 不知道它将如何影响行虚拟化。如果组未展开,则不会考虑渲染行,因此没有理由将它们包含在虚拟化中。
    • 询问“不要丢弃默认值”,您可以做的是从 github 复制它并将其用作自己的起点?或者如果你问我是否可以扩展我拥有的东西,是的,我可以,而且我想这样做,它现在不在我的列表的顶部。
    • 我已经玩了一点树演示。我不是在这里要求一个功能,我只是想收集一些数据来看看 ag-grid 是否适合我正在开发的产品。您是说如果我要制作自己的组渲染器,那不会影响虚拟化(如果我在扩展节点上有 2000 个叶子)?你是说编写我自己的 groupRowRenderer 是解决这种情况的方法?
    【解决方案3】:

    只是一个想法,但我认为您可以在第一个单元格中使用“正在加载...”的组中添加一个占位符子行,并使用该组的 onRowGroupOpened 事件设置为进行 ajax 调用以从服务器获取数据,onreadystatechange 然后添加新行并替换占位符。初始占位符行可以包含服务器计算的总值,以驱动组行单元格中的聚合(总计)值,当实际数据替换占位符时,这些值将保持不变。

    我已经对这种方法进行了基本测试。这并不完美,因为每次扩展后网格都会重建(我找不到一种优雅的方式来追加新行),但它确实有效。

    脚本的最顶部是 AJAX 调用的详细信息。虽然这发生在流程的后面,但我将它放在顶部,以便如果服务器收到此请求,它会提供数据并退出,而无需再次加载页面。或者,您可以将其放入另一个文件中。

    <?php
    if (isset($_REQUEST['g'])) { // this is the AJAX request for child data (called later, but needed at the start of the script)
        // get connection to database
        require_once 'db_connection.php'; $dbh=getConnection();
        // query data to array
        $sql="SELECT accounts.description AS account, '' AS info, 
              tx.amnt AS amount, 1 AS transactions
              FROM tx 
              INNER JOIN accounts ON tx.account=accounts.account_id
              WHERE accounts.description='".$_REQUEST['g']."'";
        $data=array();
        $result = $dbh->query($sql);
        while ($row = $result->fetch_assoc()) {
            $data[]=$row;
        }
        $result->free();
        // return data as JSON
        print json_encode($data, JSON_NUMERIC_CHECK);
        exit;
    }
    ?>
    

    紧接着是一个普通的 HTML 页面,头部的 javascript 中包含更多的 php:

    <!DOCTYPE html>
    <html>
    <head>
    <script src="lib/ag-grid-enterprise-master/dist/ag-grid-enterprise.js"></script>
    <script>
    // get JSON for initial group-level data from server with a little snippet of php which is called when the page is first loaded
    var rowData =
    <?php
        // get connection to the database
        require_once 'db_connection.php'; $dbh=getConnection();
        // query data to array
        $sql = "SELECT description AS account, 'loading...' AS info,
                SUM(tx.amnt) AS amount, COUNT(tx.tx_id) AS transactions
                FROM accounts 
                INNER JOIN tx ON accounts.account_id=tx.account
                GROUP BY accounts.account_id";
        $data=array();
        $result = $dbh->query($sql);
        while ($row = $result->fetch_assoc()) {
            $data[]=$row;
        }
        $result->free();
        // inject the JSON into the javascript assignment to rowData
        print json_encode($data, JSON_NUMERIC_CHECK);
    ?>;
    // (back in javascript again)
    
    // event function for when a group is expanded
    function getChildRows(data) {
        if (data.node.allLeafChildren) {
            if (data.node.allLeafChildren.length > 0) {
                if (data.node.allLeafChildren[0].data.info==="loading...") {
                    // data for this group has not yet been loaded, so make AJAX request for it
                    var xmlHttp=new XMLHttpRequest();
                    xmlHttp.onreadystatechange=function() {
                        if ((xmlHttp.readyState===4) && (xmlHttp.status === 200)) {
                            // call function to add the new rows to the grid
                            addRecords(JSON.parse(xmlHttp.responseText));
                        }
                    };
                    var requestParameters="g="+encodeURIComponent(data.node.key);
                    xmlHttp.open("POST", "index.php", true);    // call to this same script
                    xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                    xmlHttp.send(requestParameters);
                }
            }
        }
    }
    function addRecords(data) {
        var x; var d=new Array();
        var acc=data[0].account;
        for(x in gridOptions.api.inMemoryRowModel.rootNode.allLeafChildren) {
            if (gridOptions.api.inMemoryRowModel.rootNode.allLeafChildren[x].data.account===acc) {
                // this is group we are replacing with new data
                for (x in data) {
                    d.push(data[x]);
                }
            } else {
                // this node is just the data as currently loaded to the grid (no change)
                d.push(gridOptions.api.inMemoryRowModel.rootNode.allLeafChildren[x].data);
            }
        }
        gridOptions.api.setRowData(d);
    }
    // set up the grid (standard stuff)
    var columnDefs = [
        {headerName: "Account", field: "account", rowGroupIndex: 0, cellRenderer: "group", cellRendererParams : {suppressCount: true} },
        {headerName: "Info", field: "info"},
        {headerName: "Amount", field: "amount", aggFunc:"sum"},
        {headerName: "Transactions", field: "transactions", aggFunc:"sum"}
    ];
    var gridOptions = {
        columnDefs: columnDefs,
        rowData: rowData,
        groupSuppressAutoColumn: true,
        onRowGroupOpened: getChildRows  /* event created above */
    }
    document.addEventListener("DOMContentLoaded", function() {
        var eGridDiv = document.querySelector('#myGrid');
        new agGrid.Grid(eGridDiv, gridOptions);
    });
    </script>
    </head>
    <body>
        <div id="myGrid" style="height: 100%;" class="ag-fresh"></div>
    </body>
    </html>
    

    @Niall - 关于如何更优雅地添加新行并保持组扩展状态的任何想法?

    【讨论】:

      猜你喜欢
      • 2020-11-17
      • 2021-04-08
      • 2019-06-06
      • 1970-01-01
      • 1970-01-01
      • 2019-06-07
      • 1970-01-01
      • 2020-05-09
      • 1970-01-01
      相关资源
      最近更新 更多