2015-08-27 42 views

回答

2

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

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

+0

嘿尼尔,如果我不要求太多,能否请您详细一点?由于编写你自己的groupRowRenderer会扔掉很多标准和有用的行为(扩展,折叠和所有这些),我在考虑是否有一种侵入性较小的方法。 例如,是否可以将ajax调用onRowExpand?或者节点将没有子节点(在点击/尝试扩展时)将不允许调用“onRowExpand”? – Zubzob

+0

另外,这不会影响虚拟化功能吗? – Zubzob

+0

没有看到它会如何影响行虚拟化。如果该组未扩展,则不会考虑这些行进行渲染,因此没有理由将它们包含在虚拟化中。 –

0

只是一个想法,但我认为你可以在第一个单元格添加一个占位符子行到组“载入中...”,同组的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页面多一点点的PHP的头在JavaScript中:

<!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 - 如何更优雅添加新行任何想法并保留团队扩张的状态?

2

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

解决的方法如下:

  1. 添加虚拟子行的每个顶级一行。可以为空或者可以在第一列中加载...字符串。

  2. 在事件getNodeChildDetails(每当您更新表rowData时调用)时,您可以指定是否应该展开行。所以我们的想法是,我们跟踪什么是扩大和什么不是。

    getNodeChildDetails = (rowItem) => { 
        if (rowItem.children) { 
        return { 
         group: true, 
         expanded: rowItem.id in this.expandedRows, 
         children: rowItem.children, 
        }; 
        } 
        else { 
        return null; 
        } 
    }; 
    
  3. On事件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); 
        };