Ext JS to React: Tree

   JavaScript
Ext JS to React: Tree

This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.

Not all data can be presented in a simple grid since not all data is flat. Take, for example, the file system on your computer. Folders can hold files as well as other nested folders, allowing your data to be organized in a hierarchical (or tree) shape. In Ext JS, a tree panel is effectively a grid with a column that displays the names of the files and folders along with an icon used to toggle the collapse state of each parent node. With ag-Grid, the same is true. We’ve been using ag-Grid as a pre-built solution to display grids and we’ll continue to use it to display a tree.

Note: While many features from ag-Grid are free, displaying data in a tree with ag-Grid is part of their enterprise pricing model. For this demonstration, you can sign up for a trial enterprise license.

Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.

ag-Grid setup

First, install ag-Grid including the files we’ll use to theme the grid:

npm install --save ag-grid
npm install --save ag-grid-react
npm install --save ag-grid-enterprise

If you’re using React 16+ you may also need to install react-dom-factories:

npm install --save react-dom-factories

Sample Tree Data

Below is the data we’ll use for our two tree examples:

const data = [
  {
    orgHierarchy: ['Erica Rogers'],
    jobTitle: 'CEO',
    employmentType: 'Permanent',
    email: 'erica.rogers@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett'],
    jobTitle: 'Exec. Vice President',
    employmentType: 'Permanent',
    email: 'malcolm.barrett@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Esther Baker'],
    jobTitle: 'Director of Operations',
    employmentType: 'Permanent',
    email: 'esther.baker@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Esther Baker', 'Brittany Hanson'],
    jobTitle: 'Fleet Coordinator',
    employmentType: 'Permanent',
    email: 'brittany.hanson@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Esther Baker', 'Brittany Hanson', 'Leah Flowers'],
    jobTitle: 'Parts Technician',
    employmentType: 'Contract',
    email: 'leah.flowers@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Esther Baker', 'Brittany Hanson', 'Tammy Sutton'],
    jobTitle: 'Service Technician',
    employmentType: 'Contract',
    email: 'tammy.sutton@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Esther Baker', 'Derek Paul'],
    jobTitle: 'Inventory Control',
    employmentType: 'Permanent',
    email: 'derek.paul@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Francis Strickland'],
    jobTitle: 'VP Sales',
    employmentType: 'Permanent',
    email: 'francis.strickland@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Francis Strickland', 'Morris Hanson'],
    jobTitle: 'Sales Manager',
    employmentType: 'Permanent',
    email: 'morris.hanson@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Francis Strickland', 'Todd Tyler'],
    jobTitle: 'Sales Executive',
    employmentType: 'Contract',
    email: 'todd.tyler@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Francis Strickland', 'Bennie Wise'],
    jobTitle: 'Sales Executive',
    employmentType: 'Contract',
    email: 'bennie.wise@acme.com'
  },
  {
    orgHierarchy: ['Erica Rogers', 'Malcolm Barrett', 'Francis Strickland', 'Joel Cooper'],
    jobTitle: 'Sales Executive',
    employmentType: 'Permanent',
    email: 'joel.cooper@acme.com'
  }
];

The data structure is flat. The orgHierarchy array is what is used to build the parent <-> child relationship. Effectively, that array is the hierarchy. ag-Grid loops through the array, finds the matching node as a parent and adds data to that parent. The other fields in the data are for the last entry in the orgHierarchy array.

ag-Grid Tree Configuration

With Ext JS, the TreeStore does most of the data handling. It creates all the records and nests them in childNodes array. This is almost exactly how JavaScript manages the DOM. Child nodes are in an element’s childNodes property. With ag-Grid, the data is flat, but is given an array to build the hierarchy for you automatically. Below is an example of the Tree component:

import React, { Component } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid/dist/styles/ag-grid.css';
import 'ag-grid/dist/styles/ag-theme-material.css';
import 'ag-grid-enterprise';

import {LicenseManager} from "ag-grid-enterprise/main";
// The tree view from ag-Grid requires an enterprise license and a product key
// Pricing: https://www.ag-grid.com/license-pricing.php
// Trial: https://www.ag-grid.com/start-trial.php
LicenseManager.setLicenseKey("your license key");

class App extends Component {
  columns = [
    {
      field: 'jobTitle'
    },
    {
      field: 'employmentType'
    }
  ]

  getDataPath = (data) => {
    return data.orgHierarchy;
  }

  autoGroupColumnDef = {
    headerName: 'Organization Hierarchy',
    width: 300,
    cellRendererParams: {
      suppressCount: true
    }
  }

  render () {
    return (
      <div className="ag-theme-material">
        <AgGridReact
          containerStyle={{height: '400px'}}
          columnDefs={this.columns}
          rowData={data}
          treeData={true}
          getDataPath={this.getDataPath}
          autoGroupColumnDef={this.autoGroupColumnDef}
        />
      </div>
    );
  }
}

export default App; 



This looks very similar to the grids we’ve configured before, but there are three key configs:

  • treeData: This is pretty self documenting, but it tells the grid that it will be working with data to display a tree
  • getDataPath: A function that will return the array that will be used to build the hierarchical data
  • autoGroupColumnDef: A column definition (like what we have in the columns array) that will be added to display the tree UI bits like the expand / collapse icon. This column can be thought of as the “tree column”.
Ext JS to React: Tree, Simple



You can review the sample code on the Git repo.

ag-Grid Tree Features

As we’ve seen in the Features and Plugins article, ag-Grid’s grid component offers advanced features above and beyond the basic implementation. Since the ag-Grid tree is just a grid, the same features we’ve seen used with the grid can be used by the tree:

import React, { Component } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid/dist/styles/ag-grid.css';
import 'ag-grid/dist/styles/ag-theme-material.css';
import 'ag-grid-enterprise';

import {LicenseManager} from "ag-grid-enterprise/main";
// The tree view from ag-Grid requires an enterprise license and a product key
// Pricing: https://www.ag-grid.com/license-pricing.php
// Trial: https://www.ag-grid.com/start-trial.php
LicenseManager.setLicenseKey("your license key");

class App extends Component {
  columns = [
    {
      field: 'jobTitle',
      editable: true
    },
    {
      field: 'employmentType'
    },
    {
      field: 'email',
      cellRenderer: params => {
        return `<a href="mailto:${params.value}">${params.value}</a>`;
      }
    }
  ]

  getDataPath = (data) => {
    return data.orgHierarchy;
  }
  autoGroupColumnDef = {
    headerName: 'Organisation Hierarchy',
    width: 300,
    pinned: 'left',
    editable: true,
    cellRendererParams: {
      suppressCount: true
    }
  }

  render () {
    return (
      <div className="ag-theme-material">
        <AgGridReact
          containerStyle={{height: '400px', width: '700px'}}
          columnDefs={this.columns}
          rowData={data}
          treeData={true}
          getDataPath={this.getDataPath}
          autoGroupColumnDef={this.autoGroupColumnDef}
          enableGroupEdit={true}
        />
      </div>
    );
  }
}

export default App; 

In this example, we enabled cell rendering, column locking, and cell editing. For cell rendering, there is a new “email” column that uses the cellRenderer to return a clickable link. The first column showing the tree icons is also pinned to the left so you can scroll to see the other columns, yet always see who the data belongs to. To enable cell editing, we added editable config to both the “job title” and the “tree” column. It will render the default text editor on double-click. To enable double-click editing, we had to prevent the tree from expanding the node on double-click using the enableGroupEdit prop.

Ext JS to React: Tree, Advanced



You can review the sample code on the Git repo.

Conclusion

Just as we used ag-Grid to create a grid matching Ext JS’s grid, we can use ag-Grid to match Ext JS’s tree as well. Once again, we see that the transition from Ext JS to React isn’t as scary as it first seems. The tree we used here isn’t free. However, ag-Grid’s enterprise pricing does allow single licenses and comes with a two-month free trial.

Have you used a grid or tree component in React that you love? As always, we’d like to hear from you! Please feel free to share any resources you’ve discovered for tabular / hierarchical data using the comments below!


Like What You See?

Got any questions?


>