Responsive Datagrid component in Lightning – Basics

This blog post explains simple use case of Nested Lightning Component. It goes through example on how to interact and combine nested components to be used in parent Lightning component.

Please refer this post of latest version of responsive Datagrid Lightning Component.

This is a first blog post of a series, to develop advance lightning components and understand various features offered by Salesforce Lightning Platform. This post will explain why do we need nested components and how do they communicate.

At first, nested components looks like displaying something in child component and wrapped in parent component. However, to make most of nested components and designing efficient Lightning components, we should use nested component only to define set of rules. Unlike components in Visualforce, where we render some reusable output on page, Nested components in Lightning defines set of attributes to be used by parent component.

Hard to digest and imagine ? Please read on this post, you will understand concept soon.

Below image shows output of component we will be building here.

Using Nested Lightning component to create responsive DataGrid
Using Nested Lightning component to create responsive DataGrid

To start with responsive datagrid components, we need to know

  • How many columns to show
  • Heading of columns
  • Column to hide in smaller screen (to get responsive behavior)
  • Data in each row
  • Unique identifier on each row

we will start with first component to define attributes about columns in responsive table.

DataColumn.cmp

<aura:component >	
    <aura:attribute name="type" type="String" required="false" default="text" description="Type of the column to display in Datagrid" />    
    <aura:attribute name="label" type="String" required="true" default="Label" description="Label of the column to display in Datagrid" />    
    <aura:attribute name="class" type="String" required="false" description="CSS class of the column to display in Datagrid" />    
    <aura:attribute name="hidden" type="Boolean" required="false" default="false" description="Hide this column in mobile mode or not" />      
</aura:component>

This component contains information about columns datatype, heading, css class and attribute to inform that it needs to be hidden in case of small screen.

DataRow.cmp

<aura:component >
    <aura:attribute name="data" type="String" required="true" default="text" description="values in row" />   
    <aura:attribute name="delimiter" type="String" required="false" default="|" description="delimiter to seperate values in row" />   
    <aura:attribute name="uk" type="String" required="true" description="unique key of row" />    
</aura:component>

Above component defines properties about each row, like data to be rendered, delimiter to separate contents as a column and unique key in row. We will not be unique key in this post however, in coming post this attribute will prove to be very important.

Both components declared above, does not perform any fancy or heavy lifting other than just declaring some attributes.

Next component will perform rendering task by applying necessary CSS from Saleforce Lightning design system. This component assumes that it will get list of data rows and column information. It does not care about anything else. That’s advantage of Lightning components, Think in a small step and provide proper loose coupling and re-usability.

DataGridTable.cmp

<aura:component >
	
    <aura:attribute name="rows" type="Object[]" description="rows of table" />
    <aura:attribute name="cols" type="Object[]" description="Columns of table" />    

<div class="slds">
<table class="slds-table slds-table--bordered">

<thead>
<tr class="slds-text-heading--label">            	
                 <aura:iteration items="{!v.cols}" var="col">
<th class="{!col.class}"> {!col.label} </th>
                </aura:iteration>                
            </tr>        	 
        </thead>            

<tbody>
            <aura:iteration items="{!v.rows}" var="row">

<tr class="slds-hint-parent"> 
                	<aura:iteration items="{!row.dataColumns}" var="colData">

<td class="{!colData.class}">
                        	{!colData.data}
                        </td>

                    </aura:iteration>
                </tr>

             </aura:iteration>              
        </tbody>
    
    	</table>
    </div>
 
</aura:component>

It uses aura:iterator component from Salesforce to loop through each row and column. This component also assumes that each row and column has attributes defined as per “DataRow” and “DataColumn” component.

Giving responsive behavior to columns in table
Component “DataGridTable” assumes that column which needs to be hide has CSS class named “hidden”. Again, this component does not care about how this CSS class name assigned to this column. we will be using @media query of CSS to detect screen size and apply CSS accordingly.

DataGridTable.css

@media(max-width:700px){
    .THIS .hidden{
        display : none !important;
    }
}

Above CSS defines that if screen size is not more than 700px (means it is not viewed in desktop), then hide all elements which has CSS class “hidden”.

Before going ahead, let’s see how we are going to use combination of nested components to render responsive table.

ResponsiveTableDemo.app

<aura:application >
    
    <c:DataGrid >
        <c:DataColumn label="Name" type="text" />
        <c:DataColumn label="DOB" type="date" />
        <c:DataColumn label="Email" type="Text" class="hidden" />
        <c:DataColumn label="Mobile" type="Number" class="hidden" />
        
        <c:DataRow data="Rudra|Jan-11|abc@xyz.com|1234569878" delimiter="|" uk="1" />
        <c:DataRow data="Shivanya|Feb-17|yuy@pqr.com|1234569878" delimiter="|" uk="2" />
        <c:DataRow data="Minal;Dec-31;ghy@sds.com;1234569878" delimiter=";" uk="3"/>         
    </c:DataGrid> 
    
</aura:application>

We have not yet created “DataGrid” component, however you can get an idea how it will be working. “DataGrid” will contain two nested components “DataColumn” and “DataRow”. If you have observed, component “DataGridTable” is not used here as it is not nested component (because it perform rendering operation). Nested components does not perform any operation except declaring some important attributes.

Its time to get into action and perform all heavy lifting. Till now, all components defined above are either defining some attributes (Nested components) or simply render table. However, we need to read data and nourish it so that can be used by “DataGridTable” component to render it.

DataGrid.cmp

<aura:component >
    <ltng:require styles="/resource/SLDS_1_0/assets/styles/salesforce-lightning-design-system-ltng.css" />
    
    <aura:attribute name="cols" type="Object[]" description="attribute to hold cols" />  
    <aura:attribute name="rows" type="object[]" description="attribute to hold rows" />
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" /> 
    <c:DataGridTable rows="{!v.rows}" cols="{!v.cols}" />
    
</aura:component>

We have defined two attributes named “Cols” and “rows”. When component will be loaded, doInit method will be executed.

DataGridController.js

({
	doInit : function(component, event, helper) {
		helper.OnInit(component, event, helper);
	}
})

As per best practice of lightning component, we will not be writing any logic in client side controller of component. We will take help of Helper file as shown below.

DataGridHelper.js

({
	OnInit : function(component, event, helper) {         
        var obj = this.parseTableBody(component);
        component.set("v.rows", obj.rows);	
        component.set("v.cols" , obj.cols);	
	},
    
    parseTableBody : function(component){
        
        var body = 	component.get("v.body");	
        
        colItems = [] ;
        var  rowData = [], rowDataItems = [] , rowItems = [];
        
        var result , currentTag ;
        
        for(var i = 0 ; i<body.length ; i++){
            currentTag = body[i] ;
            
            switch(currentTag.getDef().getDescriptor().getName()){
                case 'DataRow' :
                    rowData = currentTag.get("v.data").split(currentTag.get("v.delimiter"));
                    rowDataItems = [] ;
                    
                    for(var j = 0; j<rowData.length ; j++){
                        rowDataItems.push({
                            data : rowData[j],
                            class : colItems[j].class
                        });  
                    }
                    rowItems .push({
                        dataColumns : rowDataItems,
                        uk : currentTag.get("v.uk")
                    });
                     
                    break ;
                    
                case 'DataColumn' :
                    colItems.push({
                        label : currentTag.get("v.label") ,
                        type : currentTag.get("v.type"),
                        class : currentTag.get("v.class")
                    });
                    break ;                    
            }            
        }
        
        return {
            rows : rowItems,
            cols : colItems
        };        
    }
})

This helper file iterates through all nested component by using component.get(‘v.body’). It will return multiple types like DataRor or DataColumn, so we have used case statements in javascript.

To get type of nested component, we need to use currentTag.getDef().getDescriptor().getName() where currentTag variable holds reference to DOM element defined in component. After that, code is quite self explanaotry, all I am doing is to covert data into understandable format which can be used in “DataGridTable” component.

I also did, live coding of this component and recording can be watched here.

In next post, we will replace hard-coded values by actual data using server side controller (Apex controller). We will need to perform some necessary modifications in this component as well.

Hope, nested component’s concept makes sense to you after reading this post. Would appreciate your feedback.

Related posts

  • Kishlay Mathur

    Hi Jitendra sir , i am following your blog
    i implemented the datagrid lighting component as given by you , its was working fine , but after few days when i open it is giving me error as getDef() in not a function