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

*/

(function(window)

{

   "use strict";

   var TextInputRenderer, uiUtil = flexiciousNmsp.UIUtils, flxConstants = flexiciousNmsp.Constants;

   /**

    * 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

    */

   TextInputRenderer=function(){

       //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);

   };

   myCompanyNameSpace.ItemRenderers_TextInputRenderer = TextInputRenderer; //add to name space

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

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

   TextInputRenderer.prototype.getClassNames=function(){

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

   };


   TextInputRenderer.prototype.setWidth=function(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

    */

   TextInputRenderer.prototype.setData=function(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

    */

   TextInputRenderer.prototype.onChange=function(evt){

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

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

       var 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.

   TextInputRenderer.prototype.setText=function(val){


   };


}(window));




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


   '                                        <column headerText="Editable Name" dataField="legalName" '+

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

   '                                           paddingRight="8" enableCellClickRowSelect="false" itemRenderer="myCompanyNameSpace.ItemRenderers_TextInputRenderer">'+

   '                                        </column>'+



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

*/

(function(window)

{

   "use strict";

   var CheckBoxHeaderRenderer, uiUtil = flexiciousNmsp.UIUtils, flxConstants = flexiciousNmsp.Constants;

   /**

    * 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

    */

   CheckBoxHeaderRenderer=function(){

       //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);

   };

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

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

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

   CheckBoxHeaderRenderer.prototype.getClassNames=function(){

       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

    */

   CheckBoxHeaderRenderer.prototype.onChange=function(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.


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

       var 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.

       var 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 (var 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.

   CheckBoxHeaderRenderer.prototype.setText=function(val){


   };

}(window));



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

*/

(function(window)

{

   "use strict";

   var NextLevelRenderer2, uiUtil = flexiciousNmsp.UIUtils, flxConstants = flexiciousNmsp.Constants;

   /**

    * 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

    */

   NextLevelRenderer2=function(){

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

   };

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

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

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

   NextLevelRenderer2.prototype.getClassNames=function(){

       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

    */

   NextLevelRenderer2.prototype.setData=function(val){

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


       var 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);

   }

}(window));


myCompanyNameSpace.levelRenderers2_creationCompleteHandler =function (evt){

   grid.validateNow();

   grid.expandAll();

}

myCompanyNameSpace.SAMPLE_CONFIGS["LevelRenderers2"]='<grid id="grid" enablePrint="true"  enableDrillDown="true"'+

   '                                                                         enablePreferencePersistence="true"'+

   '                                                                         enableExport="true" enableCopy="true"'+

   '                                                                         preferencePersistenceKey="levelRenderers2" on'+flexiciousNmsp.Constants.EVENT_CREATION_COMPLETE+'="myCompanyNameSpace.levelRenderers2_creationCompleteHandler">'+

   '                        <level enableFilters="true" enablePaging="true" rendererHorizontalGridLines="true"  ' +

   '           rendererVerticalGridLines="true" pageSize="20" childrenField="deals" enableFooters="true" selectedKeyField="id" ' +

   '           nextLevelRenderer="myCompanyNameSpace.LevelRenderers2_NextLevelRenderer2" levelRendererHeight="120">'+

   '                                <columns>'+

   '                                        <column type="checkbox"  />'+

   '                                        <column enableCellClickRowSelect="false" columnWidthMode="fitToContent" selectable="true" dataField="id" headerText="ID" filterControl="TextInput"/>'+

   '                                        <column truncateToFit="true"  enableCellClickRowSelect="false" columnWidthMode="fitToContent"  selectable="true" dataField="legalName" headerText="Legal Name"/>'+

   '                                        <column dataField="headquarterAddress.line1" headerText="Address Line 1" footerLabel="Count:" footerOperation="count"/>'+

   '                                        <column dataField="headquarterAddress.line2" headerText="Address Line 2"/>'+

   '                                        <column dataField="headquarterAddress.city.name" headerText="City" filterControl="MultiSelectComboBox" filterComboBoxBuildFromGrid="true" filterComboBoxWidth="150"/>'+

   '                                        <column dataField="headquarterAddress.state.name" headerText="State" filterControl="MultiSelectComboBox" filterComboBoxBuildFromGrid="true" filterComboBoxWidth="150"/>'+

   '                                        <column dataField="headquarterAddress.country.name" headerText="Country" filterControl="MultiSelectComboBox" filterComboBoxBuildFromGrid="true" filterComboBoxWidth="150"/>'+

   '                                </columns>'+

   '                </level>'+

   '        </grid>';