'AG-Grid with TreeData using Enterprise Row Model

I am trying to implement a grid with tree data using ag-grid. I am using the Enterprise Row Model. The problem is that when hard coding the data and setting it through setRowData the grid displays perfectly. However, when data is loaded through the enterprise row model, the grid does not render as a tree. In fact, the getDataPath callback is not even being called.

Did anyone manage to use the tree data feature with an enterprise data source as this does not seem to be documented?

Thanks



Solution 1:[1]

I am assuming that by Enterprise row model, you mean Serverside row model so you expect tree structured data from server. In that case, I have been able to combine following features in Ag-grid : Infinite scrolling + Tree data + server side row model. I have even implemented custom filtering and it's working as expected.

Data flow:

  1. We have to enable the server side row model on ag-grid using its configuration.
  2. Implement a fake server and proxy data source objects in JavaScript. The object of data source must contain a method named "getRows" that ag-grid can call. Ag-grid will call this method every time user performs actions such as: scroll, filter, sorting, expanding a parent row to see child rows etc.
  3. Implement a method called onGridReady() which will be called by ag-grid every time it's trying to render the grid first time, and then pass the server and dataSource objects to ag-grid's internal API inside onGridReady().

Implementation (using combination of ReactJS and plain JavaScript):

  1. Enable serverSide row model in ag-grid.

    <AgGridReact
       columnDefs={this.columnDefs}
       rowModelType={this.rowModelType}
       treeData={true}
       isServerSideGroup={this.isServerSideGroup}
       getServerSideGroupKey={this.getServerSideGroupKey}
       onGridReady={this.onGridReady}
       cacheBlockSize={50}
     />
    
  2. Implement a fake server and proxy data source objects in JavaScript.

    To work with server side row model, you need to supply the data in an instance of ServerSideDataSource in JavaScript. Instance of ServerSideDataSource must have a method called getRows() which will be called by ag-grid every time user scrolls down to get next set of data or a row is expanded for retrieving its children records in Tree structure.

    The constructor for ServerSideDataSource accepts a proxy data container, typcailly used in ag-grid example: a fakeServer instance. A singleton instance of fakeServer holds the data that was received from the real server and keeps it for following usage:

    a) when ag-grid wants to display child records, it calls getRows. Because we supply a custom implementation of this ServerSideDataSource, we can write logic inside getRows to extract data from this fakeServer instance.

    b) When ag-grid tries to display next set of data in infinite scrolling or paging, it checks how much data was last retrieved to ask for next block in infinite scrolling (using startRow and endRow variable).

  3. Defining fake server and server side data source:

    function createFakeServer(fakeServerData) {
     function FakeServer(allData) {
       this.data = allData;
     }
    
     FakeServer.prototype.getData = function(request) {
       function extractRowsFromData(groupKeys, data) {
         if (groupKeys.length === 0) {
           return data; //child records are returned from here.
         }
         var key = groupKeys[0];
         for (var i = 0; i < data.length; i++) {
           if (data[i].employeeId === key) {
             return extractRowsFromData(groupKeys.slice(1), data[i].children.slice());
           }
         }
       }
       return extractRowsFromData(request.groupKeys, this.data);
     };
     return new FakeServer(fakeServerData);
    }
    
    function createServerSideDatasource(fakeServer) {
     function ServerSideDatasource(fakeServer) {
       this.fakeServer = fakeServer;
     }
    
     ServerSideDatasource.prototype.getRows = function(params) {
       console.log("ServerSideDatasource.getRows: params = ", params);
       var rows = this.fakeServer.getData(params.request);
       setTimeout(function() {
         params.successCallback(rows, rows.length);
       }, 200);
     };
    
     return new ServerSideDatasource(fakeServer);
    }
    
  4. Implement onGridReady()

    Once this dataSource is ready, you have to supply this to ag-grid by calling its API method: params.api.setServerSideDataSource(). This API is available inside onGridReady() method that must be passed to Ag-grid as well. This method is mandatorily required if you're using serverSide row model.

    onGridReady = params => {
      ...
      var fakeServer = createFakeServer(jsonDataFromServer);
      var dataSource = createServerSideDatasource(fakeServer);
      params.api.setServerSideDatasource(dataSource);
    }
    
  5. Providing a key property that help ag-grid identify parent-child relationship. You have to supply these parameters to grid. Check the point (1) in Implementation that has HTML syntax and shows how to supply these methods to ag-grid.

    var rowModelType = "serverSide";
    var isServerSideGroup = function (dataItem) {
      return !!dataItem.children;
    };
    
    var getServerSideGroupKey = function (dataItem) {
      return dataItem.employeeId;
    };
    

    Notice that in getServerSideGroup(), we are returning a boolean value which checks whether children property of current row (i.e. dataItem) has any children or not.


I would request you to separately look through documents for server side row model for each feature and that means Tree data (client model) and Tree data (server side model) have two different approaches. We can't setup one model and expect it to work with data of other model.

Documentation for server side row model : https://www.ag-grid.com/javascript-grid-server-side-model/

Please try this and let me know. I had these requirements a month ago, so I contacted them for their help on Trial Support and they have prepared a plunker for this problem statement: Working example for Tree data from server side with infinite scroll. https://next.plnkr.co/edit/XON5qvh93CpURbOJ?preview

Note:

  1. Since the tree data is being retrieved from server, you can't use getDataPath.
  2. Tree data would be in nested hierarchy per row unlike the client-side tree model. So unique column names are not encapsulated in an array.

Wrong :

var rowData = [
    {orgHierarchy: ['Erica'], jobTitle: "CEO", employmentType: "Permanent"},
    {orgHierarchy: ['Erica', 'Malcolm'], jobTitle: "VP", employmentType: "Permanent"}
    ...
]

Right :

[{
    "employeeId": 101,
    ...
    "children": [
        {
        "employeeId": 102,
        ...
        "children": [
            {
            "employeeId": 103,
            ...
            },
            {
            "employeeId": 104,
            ...
            }
         ]},
    ]}
}]
  1. There was one point when the grid was not being rendered at all in initial phase when I was just setting up the grid with Enterprise features and so it's their recommendation to use px instead of % for height and width of the wrapper DIV element that contains your Ag-grid element.

Edit:

  1. If you prefer to fetch children record in separate API calls to save initial load time then you can make these API calls inside getRows() method. API call will have success and error callbacks. Inside the success callback method, once you receive the children data, you can pass them to ag-grid using:
      params.successCallback(childrenData, childrenData.length);

Example: When a parent row is expanded, it sends a unique key of that parent row (which you must have configured already) through params.request.groupKeys. In this example, I am using JavaScript's fetch() to represent API calling. This method accepts ApiUrl, and optional request parameters' object in case of POST/PUT requests.

  ServerSideDatasource.prototype.getRows = function(params) {
    
    //get children data based on unique value of parent row from groupKeys
    if(params.request.groupKeys.length > 0) {
        fetch(API_URL, {...<required parameters>...})
        .then(response => response.json(), error => console.log(error))
        .then((childrenData) => {
            params.successCallback(childrenData, childrenData.length);
        });
    }
    else {
        //this blocks means - get the parent data as usual.
        params.successCallback(this.fakeServer.data, this.fakeServer.data.length);
    }
  };

Solution 2:[2]

  • Make sure you are using gridOptions.treeData = true An example is here.
  • Hierarchy of data should be properly set when you implement the gridOptions.getDataPath(data)
  • Make sure you have implemented Enterperise.getRows Read more

If the above things don't work, share your code here to understand the overall picture better.

Solution 3:[3]

Infinite Scrolling or Enterprise/Serverside datasources are not compatible with Tree Data https://www.ag-grid.com/javascript-grid-row-models/

So you have to either change your code to use Client Side Row Model or use Row Grouping (only available in enterprise)

Solution 4:[4]

Adding import 'ag-grid-enterprise' followed by initializing the enterprise key resolved the issue of getDataPath not working correctly.

Solution 5:[5]

Using example provided by Akshay Raut above, I was inspired to combine server side pagination, and client side grouping. Following his steps, with a slight change. On the getServerSideDatasource function, you can check if the parent node have the children, and rather than calling the server, just return the children directly. That way, you will be able to display the already loaded children as client side. Here is a sample code:

getServerSideDatasource(): IServerSideDatasource {
    return {
      getRows: (params) => {
        if (params.request.groupKeys.length > 0) {
          params.success({
            rowData: params.parentNode.data.sales,
            rowCount: params.parentNode.data.sales.length,
          });
        } else {
           // Your regular server code to get next page
       }

Here is a stackblitz: https://stackblitz.com/edit/ag-grid-angular-hello-world-1gs4jx?file=src%2Fapp%2Fapp.component.ts

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Sunil B N
Solution 3 Arikael
Solution 4 Phillip Viau
Solution 5 Mohy Eldeen