Configuration Options – Column Formatting

Parent Previous Next

So far, we have seen how to enable filters, footers, paging, print, preferences and more. In this section let’s look at a very common requirement, one that of formatting column values. Depending on your situation, there are numerous ways to customize the content of the cells. We provide you with a rich API that allows you to pretty much customize every single grid cell.


There are a number of options that the grid exposes to let you control the content of each cell:

  1. The Header cells:
    1. The default text of the header cell maps to the headerText of the column configuration. If nothing is specified, it uses the dataField of the column.
    2. If you want to customize the content of the header cell, you can use a custom headerRenderer.
  2. The Footer Cells:
    1. The default text of the footer is calculated on basis of the following logic:
      1. First we check to see if a footerLabelFunction2 is specified on the column. If so, we use it.
      2. Second, we check to see if there is a footerLabelFunction specified on the column. If so, we use it.
      3. Finally, if neither of these is specified, we use a default footer label function, which performs the following logic:
        1. Checks to see the value of the columns footerOperation. The valid values for this property are sum, min, max, count and average. On basis of this, it computes the value.
        2. It calls toFixed method passing in the footerOperationPrecision value to give it the appropriate number of decimal places
        3. If there is a footerFormater specified, it calls the format method of the footerFormatter passing in the value and displays the result.
        4. If there is a footerLabel specified, it will concatenate the value of the footer label to the result of the calculated value in step 3.
    2. If the above customization logic is not enough, there is always a mechanism to specify a custom footer renderer.
  3. The Data Cells: For the most part, this is where most of the requirements come in for customization. Based upon your needs, there are several different methods that the grid exposes for you to achieve customization of your data cells
    1. If there is a linktext property specified on the column, this function returns that.
    2. If the DataGridColumn or its DataGrid control has a non-null <code>labelFunction</code> property, it applies the function to the data object.
    3. If the DataGridColumn or its DataGrid control has a non-null <code>labelFunction2</code>property, it applies the function to the data object.
    4. Otherwise, the method extracts the contents of the field specified by the

<code>dataField</code> property, or gets the string value of the data object.

    1. If the method cannot convert the parameter to a String, it returns a single space
    2. Finally, if none of the above works for you, you can use what we call a custom item renderer. This is especially useful when there is custom functionality that you want to associate with each cell in the grid.


Now that all of this is said, let's look at what the typical markup for each of the above options looks like:

configuration:  


<ReactDataGrid id="grid" enablePrint enablePreferencePersistence enableExport forcePagerRow pageSize="50" enableFilters enableFooters >

       <ReactDataGridColumn dataField={"id"} headerText={"ID"} filterControl={"TextInput"} filterOperation={"Contains"} footerLabel={"Sum: "} footerOperation={"sum"} footerOperationPrecision={"2"} />

       <ReactDataGridColumn dataField={"type"} headerText={"Type"} filterControl={"TextInput"} filterOperation={"Contains"}

footerLabelFunction={"this.customFooterFunction"} />

       <ReactDataGridColumn dataField={""} headerText={"Label Function Example"} filterControl={"TextInput"} filterOperation={"Contains"} labelFunction={"this.labelFunctionExample"} />

       <ReactDataGridColumn dataField={""} headerText={"Label Function 2 Example"} filterControl={"TextInput"} filterOperation={"Contains"} labelFunction2={"this.labelFunction2Example"} />

       <ReactDataGridColumn dataField={"active"} headerText={"Item Renderer Example"}  

headerRenderer={"this.CheckBoxHeaderRenderer"}  filterControl={"TextInput"} filterOperation={"Contains"} itemRenderer={"this.CheckBoxRenderer"} />

</ReactDataGrid>


You will notice that we have highlighted a few important items here:

  1. For labelFunction="myCompanyNameSpace.labelFunctionExample", let's look at the actual label function. In this method, we get the item being rendered, and the column being rendered.

     this.labelFunctionExample = (item, column) => {

         return "<b> Column:"+column.getHeaderText()+" : </b> Item: " + item.type ;

     };



  1. For labelFunction2="myCompanyNameSpace.labelFunction2Example", let’s look at the actual label function – the key difference here is that we also get the cell being rendered. Each cell has a rowInfo object. The rowInfo object associated with this cell. This cell should be within the cells collection of this rowInfo object. Each cell has a corresponding rowinfo object. The rowInfo, in turn has a rowPositionInfo object. The key difference between these two objects is that there are only as many rowInfo objects as there visible rows on the page. RowPositionInfo objects on the other hand, there are as many of these as there are total items in the data provider.

myCompanyNameSpace.labelFunction2Example = function (item, column, cell)     {

         var html = "<b> Column:"+column.getHeaderText()+" : </b> Item: " + item.type + ", cell: " + cell.rowInfo.rowPositionInfo.getRowIndex();

         return html;

     };


  1. Finally, if specifying custom html is not enough for your needs, you can use itemRenderers, which is a very powerful concept. Item Renderers are basically custom JavaScript classes, that wrap regular DOM elements and sit inside the various cells of the grid. Every UI Element in the HTMLTreeGrid extends from flexiciousNmsp.UIComponent class. This is nothing but a thin wrapper that encapsulates a domElement, and provides various utility methods that allow for things like sizing, positioning, validation and invalidation, event management and display list hierarchy. In fact, even the HTMLTreeGrid class (FlexDataGrid) class eventually inherits from UIComponent. When you define your own item renderer, you have to inherit it from the flexiciousNmsp.UIComponent class. We use prototype based inheritance. The key about item renderers is that they should expose a method called setData. This method is called when the grid instantiates an itemRenderer and prepares it for display. Finally, another important thing to keep in mind is that each item renderer gets a parent property. This points to the parent FlexDataGridCell object. The actual type is FlexDataGridDataCell for itemRenderers, FlexDataGridHeaderCell for headerRenders, FlexDataGridFilterCell for filterRenderers, and FlexDataGridFooterCell for footerRenderers. Let’s take a look at what a simple item renderer looks like:


/**

* Flexicious

* Copyright 2011, Flexicious LLC

*/

/**

* A CheckBoxRenderer is a custom item renderer, that defines how to use custom cells with logic that you can control

* @constructor

* @namespace flexiciousNmsp

* @extends UIComponent

*/

class CheckBoxRenderer {

constructor() {

    //make sure to call constructor

    flexiciousNmsp.UIComponent.apply(this,["input"]);//second parameter is the tag name for the DOM element.

    this.domElement.type = "checkbox"; //so our input element becomes a checkbox;


    /**

     * This is a getter/setter for the data property. When the cell is created, it belongs to a row

     * The data property points to the item in the grids dataprovider that is being rendered by this cell.

     * @type {*}

     */

    this.data=null;


    //the add evt listener will basically proxy all DomEvents to your code to handle.

    this.addEventListener(this,flxConstants.EVENT_CHANGE,this.onChange);

}


getClassNames() {

    return ["CheckBoxRenderer","UIComponent"]; //this is a mechanism to replicate the "is" and "as" keywords of most other OO programming languages

}


/**

 * This is important, because the grid looks for a "setData" method on the renderer.

 * In here, we intercept the call to setData, and inject our logic to populate the text input.

 * @param val

 */

setData(val) {

    flexiciousNmsp.UIComponent.prototype.setData.apply(this,[val]);

    const cell = this.parent; //this is an instance of FlexDataGridDataCell (For data rows)

    const column = cell.getColumn();//this is an instance of FlexDataGridColumn.

    this.domElement.checked=this.data[column.getDataField()];

}


/**

 * This event is dispatched when the user clicks on the icon. The event is actually a flexicious event, and has a trigger event

 * property that points back to the original domEvent.

 * @param event

 */

onChange(evt) {


    //in the renderer, you have the handle to the cell that the renderer belongs to, via the this.parent property that you inherit from flexiciousNmsp.UIComponent.


    const cell = this.parent; //this is an instance of FlexDataGridDataCell (For data rows)

    const column = cell.getColumn();//this is an instance of FlexDataGridColumn.


    this.data[column.getDataField()]=this.domElement.checked;//we use the DOM element to wire back the value to the data object.

}


//This sets  the inner html, and grid will try to set it. Since we are an input field, IE 8 will complain. So we ignore it since we dont need it anyway.

setText(val) {


}

}


myCompanyNameSpace.CheckBoxRenderer = CheckBoxRenderer; //add to name space

CheckBoxRenderer.prototype = new flexiciousNmsp.UIComponent(); //setup hierarchy

CheckBoxRenderer.prototype.typeName = CheckBoxRenderer.typeName = 'CheckBoxRenderer';//for quick inspection


Similar to the above, the same concept is extended to header renderers (and footer, filter, pager as well as nextLevelRenderers). So, now that all of that is done, let’s take a quick look at the final markup for this page, and generated output:


<!DOCTYPE html>

<!--PLEASE ENSURE YOU READ THE ACCOMPANYING README.TXT FOR DETAILS-->

<html lang="en">


<head>

 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

 <title>React DataGrid Example</title>

</head>


<body>

 <div id="app"></div>

 <script src="../../lib/reactdatagrid.js"></script>

 <script src="../../data.js"></script>

 <script type="text/babel">


         var myCompanyNameSpace = {};


       myCompanyNameSpace.labelFunctionExample = function (item, column) {

           var html = "<b> Column:"+column.getHeaderText()+" : </b> Item: " + item.type ;

           return html;

       };

       myCompanyNameSpace.labelFunction2Example = function (item, column, cell) {

           var html = "<b> Column:"+column.getHeaderText()+" : </b> Item: " + item.type + ", cell: " + cell.rowInfo.rowPositionInfo.getRowIndex();

           return html;

       };

       myCompanyNameSpace.customFooterFunction = function () {

           var html = "<div> Look Mommy, custom footer!</div>";

           return html;

       };



/**

        * Flexicious

        * Copyright 2011, Flexicious LLC

        */


       /**

        * A CheckBoxRenderer is a custom item renderer, that defines how to use custom cells with logic that you can control

        * @constructor

        * @namespace flexiciousNmsp

        * @extends UIComponent

        */

       class CheckBoxRenderer {

       constructor() {

           //make sure to call constructor

           flexiciousNmsp.UIComponent.apply(this,["input"]);//second parameter is the tag name for the dom element.

           this.domElement.type = "checkbox"; //so our input element becomes a checkbox;


           /**

            * This is a getter/setter for the data property. When the cell is created, it belongs to a row

            * The data property points to the item in the grids dataprovider that is being rendered by this cell.

            * @type {*}

            */

           this.data=null;


           //the add evt listener will basically proxy all DomEvents to your code to handle.

           this.addEventListener(this,flxConstants.EVENT_CHANGE,this.onChange);

       }


       getClassNames() {

           return ["CheckBoxRenderer","UIComponent"]; //this is a mechanism to replicate the "is" and "as" keywords of most other OO programming languages

       }


       /**

        * This is important, because the grid looks for a "setData" method on the renderer.

        * In here, we intercept the call to setData, and inject our logic to populate the text input.

        * @param val

        */

       setData(val) {

           flexiciousNmsp.UIComponent.prototype.setData.apply(this,[val]);

           const cell = this.parent; //this is an instance of FlexDataGridDataCell (For data rows)

           const column = cell.getColumn();//this is an instance of FlexDataGridColumn.

           this.domElement.checked=this.data[column.getDataField()];

       }


       /**

        * This event is dispatched when the user clicks on the icon. The event is actually a flexicious event, and has a trigger event

        * property that points back to the original domEvent.

        * @param event

        */

       onChange(evt) {


           //in the renderer, you have the handle to the cell that the renderer belongs to, via the this.parent property that you inherit from flexiciousNmsp.UIComponent.


           const cell = this.parent; //this is an instance of FlexDataGridDataCell (For data rows)

           const column = cell.getColumn();//this is an instance of FlexDataGridColumn.


           this.data[column.getDataField()]=this.domElement.checked;//we use the dom element to wire back the value to the data object.

       }


       //This sets  the inner html, and grid will try to set it. Since we are an input field, IE 8 will complain. So we ignore it since we dont need it anyway.

       setText(val) {


       }

       }


       myCompanyNameSpace.CheckBoxRenderer = CheckBoxRenderer; //add to name space

       CheckBoxRenderer.prototype = new flexiciousNmsp.UIComponent(); //setup hierarchy

       CheckBoxRenderer.prototype.typeName = CheckBoxRenderer.typeName = 'CheckBoxRenderer';//for quick inspection


/**

        * Flexicious

        * Copyright 2011, Flexicious LLC

        */

       /**

        * A CheckBoxHeaderRenderer is a custom item renderer, that you can use in a header cell. In this case, we customize the header

        * so that instead of showing a header label, we show a checkbox that switches the dataField flag on all items.

        * @constructor

        * @namespace flexiciousNmsp

        * @extends UIComponent

        */

       class CheckBoxHeaderRenderer {

           constructor() {

               //make sure to call constructor

               flexiciousNmsp.UIComponent.apply(this,["input"]);//second parameter is the tag name for the dom element.

               this.domElement.type = "checkbox"; //so our input element becomes a checkbox;

               this.domElement.checked=true;

               //the add event listener will basically proxy all DomEvents to your code to handle.

               this.addEventListener(this,flxConstants.EVENT_CHANGE,this.onChange);

           }


           getClassNames() {

               return ["CheckBoxHeaderRenderer","UIComponent"]; //this is a mechanism to replicate the "is" and "as" keywords of most other OO programming languages

           }


           /**

            * This event is dispatched when the user clicks on the icon. The event is actually a flexicious event, and has a trigger event

            * property that points back to the original domEvent.

            * @param event

            */

           onChange(event) {


               //in the renderer, you have the handle to the cell that the renderer belongs to, via the this.parent property that you inherit from flexiciousNmsp.UIComponent.


               const cell = this.parent; //this is an instance of FlexDataGridDataCell (For data rows)

               const column = cell.getColumn();//this is an instance of FlexDataGridColumn.

               //var dp = cell.level.getGrid().getDataProvider();//this is a pointer back to the grid and its dataprovider.

               let dp=this.data;//for header cells, specifically in case of nested grids, the data property is a pointer back to the top level array, or the children array


               if(this.data.hasOwnProperty("deals")){

                   //this means we are at a inner level checkbox header

                   dp=this.data.deals;

               }

               //based upon which level this renderer appears.

               for (let i=0;i<dp.length;i++){

                   dp[i][column.getDataField()] = this.domElement.checked;

               }


               column.level.grid.refreshCells();//this will re-render the cells.

           }


           //This sets  the inner html, and grid will try to set it. Since we are an input field, IE 8 will complain. So we ignore it since we dont need it anyway.

           setText(val) {


           }

       }


       myCompanyNameSpace.CheckBoxHeaderRenderer = CheckBoxHeaderRenderer; //add to name space

       CheckBoxHeaderRenderer.prototype = new flexiciousNmsp.UIComponent(); //setup hierarchy

       CheckBoxHeaderRenderer.prototype.typeName = CheckBoxHeaderRenderer.typeName = 'CheckBoxHeaderRenderer';//for quick inspection


       


       ReactDOM.render(

   <Example />,

   document.getElementById('app')

);

   </script>

</head>

<body>

   <div id="gridContainer" style="height: 400px;width: 100%;">

   </div>

</body>

</html>