Custom Renderers

Parent Previous Next

Lets take a quick look at what a custom item renderer looks like. Note, this is a renderer that displays a data cell, but the same concept can be used to render custom interactive content in header, footer, filter, pager, as well as level renderer cells:


This is a renderer that uses a text input to render the data, instead of HTML label. Technically, you could just use a labelFunction and write out the HTML for the text input, but by having a renderer class do this, you have more control over interaction, as well as a handle to the cell that is rendering the data.




/**

* Flexicious

* Copyright 2011, Flexicious LLC

*/

/**

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

* @constructor

* @namespace flexiciousNmsp

* @extends UIComponent

*/

class TextInputRenderer {

   constructor() {

       //make sure to call constructor

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

       /**

        * 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 event listener will basically proxy all DomEvents to your code to handle.

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

   }


   getClassNames() {

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

   }


   setWidth(w) {

       flexiciousNmsp.UIComponent.prototype.setWidth.apply(this,[w]);

   }


   /**

    * 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]);

       this.domElement.value=val.legalName;

   }


   /**

    * 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) {

       this.data.legalName=this.domElement.value;//we use the dom element to wire back the value to the data object.

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

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

       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.ItemRenderers_TextInputRenderer = TextInputRenderer; //add to name space

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

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


The way you would associate this renderer to a column is this:


  <ReactDataGridColumn headerText="Editable Name" dataField="legalName"

      filterControl="TextInput" filterOperation="BeginsWith" paddingLeft="5" paddingBottom="5"

      paddingRight="8" enableCellClickRowSelect="false" itemRenderer={"TextInputRenderer"}>

  </ReactDataGridColumn>



Just like you associate renderers with data cells, you can do the same with header, footer, as well as filter cells.


Lets take a look at what a header renderer looks like:


/**

* 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.ItemRenderers_CheckBoxHeaderRenderer = CheckBoxHeaderRenderer; //add to name space

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

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



Again, you can specify HTML for the headerText, but you may choose to use a JavaScript based renderer if you need more control over the interaction of the component within the HTML.



In the Demo Console, you can review the "Item Renderers" example for a running demo of how to use renderers.





The same concept applies to other kinds of renderers, including:




For example, lets take a quick look at how we define the nextLevelRenderer. In the demo console, there is a running version of this example, that shows the inner level custom display:


/**

* Flexicious

* Copyright 2011, Flexicious LLC

*/

/**

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

* @constructor

* @namespace flexiciousNmsp

* @extends UIComponent

*/

class NextLevelRenderer2 {

   constructor() {

       //make sure to call constructor

       flexiciousNmsp.UIComponent.apply(this);//second parameter is the tag name for the dom element.

       this.setHeight(50);

       /**

        * 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;

   }


   getClassNames() {

       return ["NextLevelRenderer2","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 render the html for the renderer.

    * @param val

    */

   setData(val) {

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


       const html = `<fieldset><legend>Orgainzation Information</legend><table style='width:100%'><tr><td style='border:solid 1px #000000'>Organization Name ${val.legalName} </td><td style='border:solid 1px #000000'>Sales Contact ${val.salesContact.getDisplayName()} </td><td style='border:solid 1px #000000'>Sales Contact Phone:${val.salesContact.telephone} </td></tr><tr><td style='border:solid 1px #000000''>Annual Revenue:${flexiciousNmsp.UIUtils.formatCurrency(val.annualRevenue)} </td><td style='border:solid 1px #000000''>EPS:${flexiciousNmsp.UIUtils.formatCurrency(val.earningsPerShare)} </td><td style='border:solid 1px #000000''>Last Stock Price:${flexiciousNmsp.UIUtils.formatCurrency(val.lastStockPrice)} </td></tr><tr><td style='border:solid 1px #000000''>Employees:${val.numEmployees} </td><td colspan='2' style='border:solid 1px #000000''>Address:${val.headquarterAddress.toDisplayString()} </td></tr></table></fieldset>`;


       this.setInnerHTML(html);

   }

}


myCompanyNameSpace.LevelRenderers2_NextLevelRenderer2 = NextLevelRenderer2; //add to name space

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

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



class Example extends React.Component {

   constructor() {


       this.addEventListener(flexiciousNmsp.Constants.EVENT_CREATION_COMPLETE, levelRenderers2_creationCompleteHandler);

   }


   levelRenderers2_creationCompleteHandler(evt) {

       this.refs.grid.validateNow();

       this.refs.grid.expandAll();

   }


   render() {

       return (

           <ReactDataGrid ref={(grid) => {this.grid = grid}} enablePrint enableDrillDown enablePreferencePersistence

                                               enableExport enableCopy preferencePersistenceKey={"levelRenderers2"} >

               <ReactDataGridColumnLevel enableFilters enablePaging rendererHorizontalGridLines

               rendererVerticalGridLines pageSize={"20"} childrenField={"deals"} enableFooters selectedKeyField={"id"}

               nextLevelRenderer={"NextLevelRenderer2"} levelRendererHeight={"120"} >

                       <ReactDataGridColumnGroup>

                           <ReactDataGridColumn type={"checkbox"} />

                           <ReactDataGridColumn enableCellClickRowSelect={"false"} columnWidthMode={"fitToContent"}

                                       selectable dataField={"id"} headerText={"ID"} filterControl={"TextInput"} />

                           <ReactDataGridColumn truncateToFit enableCellClickRowSelect={"false"} columnWidthMode={"fitToContent"}

                                       selectable dataField={"legalName"} headerText={"Legal Name"} />

                           <ReactDataGridColumn dataField={"headquarterAddress.line1"} headerText={"Address Line 1"}

                                       footerLabel={"Count:"} footerOperation={"count"} />

                           <ReactDataGridColumn dataField={"headquarterAddress.line2"} headerText={"Address Line 2"} />

                           <ReactDataGridColumn dataField={"headquarterAddress.city.name"} headerText={"City"}

                                       filterControl={"MultiSelectComboBox"} filterComboBoxBuildFromGrid filterComboBoxWidth={"150"} />

                           <ReactDataGridColumn dataField={"headquarterAddress.state.name"} headerText={"State"}

                                       filterControl={"MultiSelectComboBox"} filterComboBoxBuildFromGrid filterComboBoxWidth={"150"} />

                           <ReactDataGridColumn dataField={"headquarterAddress.country.name"} headerText={"Country"}

                                       filterControl={"MultiSelectComboBox"} filterComboBoxBuildFromGrid filterComboBoxWidth={"150"} />

                       </ReactDataGridColumnGroup>

               </ReactDataGridColumnLevel>

           </ReactDataGrid>

       );

   }

}