[SalesForce] client side sorting in dynamic html table aura component

I have created a lightning component that iterates over column data and row data and in between this iteration i am calling my other lightning component that basically generates data for the table. The code looks like this:

<table class="slds-table slds-table--bordered slds-table--cell-buffer slds-box" style="height;300px">
    <thead>
        <tr class="slds-text-heading--label">
            <aura:iteration items="{!v.columns}" var="ct">
                <th scope="col" class="slds-is-sortable">
                    <div class="slds-truncate" title="{!ct.label}" onclick="{!c.sortAction}">{!ct.label}</div>
                </th>
            </aura:iteration>
        </tr>
    </thead>
    <tbody>
        <aura:iteration items="{!v.data}" var="acc">
            <tr>
                <aura:iteration items="{!v.columns}" var="col">
                    <td><c:DataGrigCell data="{!acc}" column="{!col}" relatedAssociations="{!v.relatedAssociations}" relatedContacts="{!v.relatedContacts}"/></td>  
                </aura:iteration>
            </tr>
        </aura:iteration>
    </tbody>
</table>

Now i want to know how can i implement client side sorting in this table in lightning component. Any kind of help is appreciated. Thanks in advance 🙂

Best Answer

You should try Lightning:DataTable with standard table features. You can use this generic component as well.

CmAp_LightningDataTable.Cmp

<aura:component >
    <!-- Attrubutes Definations Start here -->
    <aura:attribute type="boolean"
                    name="enableSearchonRow"
                    description="This attribute is used to show hide search option in tabel"/>
    <aura:attribute type="Integer"
                    name="maxRowSelection"
                    default="1"
                    description="This attribute is used to define number of row can be selected"/>
    <aura:attribute type="string"
                    name="tableHeaderTitle"
                    default=""
                    description="This attribute is used to define title for table"/>
    <aura:attribute type="List"
                    name="selectedRows"
                    description="This attribute is used to store preselected record Ids"
                    />
    <aura:attribute type="string"
                    name="panelHeightinPx"
                    default="170px"
                    description="This attribute is used to define container Height"
                    />
    <aura:attribute type="string"
                    name="headerIcon"
                    default="standard:empty"
                    description="This attribute is used to define icon in Header"
                    />
    <aura:attribute type="boolean"
                    name="showRowNumber"
                    default="false"
                    description="This attribute is used to show hide row number"
                    />
    <aura:attribute type="boolean"
                    name="showHeaderButton"
                    default="false"
                    description="This attribute is used to show hide button in Header"
                    />
    <aura:attribute type="string"
                    name="buttonLabel"
                    default="New"
                    description="This attribute is used to show label on Button"
                    />
    <aura:attribute type="string"
                    name="searchString"
                    default="false"
                    description="This attribute store search fields for row filter. Fields must be comma saperated."
                    />
    <aura:attribute name="recordSearchKey" 
                    type="string" 
                    default=""
                    description="this attribute store search string for row in table"
                    access="Private"/>
    <aura:attribute name="unFilteredData"
                    type="List"
                    description="this attribute store all data for table without filter"
                    access="Private"
                    /> 
    <aura:attribute name="lstDataRows"
                    type="List"
                    Description="attribute store Data Rows list of sobject"/>
    <aura:attribute name="lstColumns"
                    type="List"
                    Description="store Header columns property list"/> 
    <aura:attribute name="defaultSelectedRow"
                    type="string"
                    Description="store default selected row id in table"/> 
    <aura:attribute name="showSpinner"
                    type="Boolean" 
                    default ="false" 
                    access="private"/>
    <aura:attribute name="sortDirection"
                    type="String"
                    default="desc" />
    <aura:attribute name="sortedBy"
                    type="String" />
    <!-- Attrubutes Definations ends here -->
    <!-- Aura Handeler Section -->
    <aura:handler name="init" value="{!this}" action="{!c.doinit}"/>
    <aura:method name="setPredefinedValues" action="{!c.setPredefinedValues}" access="public"/>
    <!--<aura:handler name="change" value="{!v.lstDataRows}" action="{!c.setPredefinedValues}"/>-->
    <!-- Aura Handeler Section Ends here -->
    <!-- Events Regstration Sections Defiend here -->
    <aura:registerEvent name="onclickActionEvent" type="c:CmAp_LightningDataTableActionEvent"/>
    <aura:registerEvent name="onRowChangeEvent" type="c:CmAp_LightningDataTableSelectionEvent"/>
    <aura:registerEvent name="onClickRowActionEvent" type="c:CmAp_LightningDataTableRowActionEvent"/>
    <!-- Events Regstration Sections Ends here -->
    <lightning:card title="{!v.tableHeaderTitle+' ('+if(not(empty(v.lstDataRows)),v.lstDataRows.length,0)+')'}" iconName="{!v.headerIcon}" variant="base">
        <aura:set attribute="actions">
            <aura:if isTrue="{!v.enableSearchonRow}">
                <lightning:input type="search" variant=" label-hidden" class="slds-m-left_small slds-m-right_small" placeholder="{!'Search '+v.tableHeaderTitle}" value="{!v.recordSearchKey}" onchange="{!c.searchInRows}" />
            </aura:if>
            <aura:if isTrue="{!v.showHeaderButton}">
                <lightning:button class="slds-theme_neutral" variant="brand" label="{!v.buttonLabel}" onclick="{!c.handelBttonClickAction}"></lightning:button>
            </aura:if>
        </aura:set>
        <aura:set attribute="footer">&nbsp;</aura:set>
        <div style="{!if(not(empty(v.lstDataRows)),'','display:none')}">
            <div style="{!if((v.lstDataRows.length> $Label.c.CmAp_AutoHeightMaxRowLimit),'height:'+v.panelHeightinPx,'')}">
                <!-- Spinner Section -->
                <aura:if isTrue="{!v.showSpinner}">
                    <div class="slds-is-relative">
                        <lightning:spinner variant="brand" alternativeText="Loading..." />
                    </div>
                </aura:if>
                <!-- Spinner Ends Here -->
                <lightning:datatable aura:id="recordTabel"
                                     columns="{!v.lstColumns}"
                                     data="{!v.lstDataRows}"
                                     keyField="Id"
                                     selectedRows="{!v.selectedRows}"
                                     maxRowSelection="{! v.maxRowSelection }"
                                     onrowselection="{!c.updateSelectedText}"
                                     showRowNumberColumn="{!v.showRowNumber}"
                                     sortedDirection="{!v.sortDirection}"
                                     sortedBy="{!v.sortedBy}"
                                     onsort="{!c.handleSort}"
                                     wrapTextMaxLines="2"
                                     hideCheckboxColumn="{!if(v.maxRowSelection>0,false,true)}"
                                     onrowaction="{!c.rowOnClickActions}"
                                     />
            </div>
        </div>

        <div class="slds-box slds-theme_default slds-align_absolute-center" style="{!if(not(empty(v.lstDataRows)),'display:none','')}">
            <p><b>No data to display.</b></p>
        </div>

    </lightning:card>
</aura:component>

CmAp_LightningDataTableController.Js

    ({
        doinit: function(component, event, helper){
            component.set("v.showSpinner",true);

        },
        setPredefinedValues : function(component, event, helper) {
            helper.setPredefinedValues(component, event, helper);
        },
        updateSelectedText: function (component, event, helper) {
            helper.handleSelect(component, event, helper);
        },
        searchInRows: function (component, event, helper) {
            helper.searchInRows(component, event, helper);
        },
        handleSort: function(component, event, helper) {
            helper.handleSort(component, event);
        } ,
        handelBttonClickAction: function (component, event, helper) {
            helper.handelBttonClickAction(component, event, helper)
        },
        rowOnClickActions: function (component, event, helper) {
            helper.rowOnClickActions(component, event, helper)
        }
    })

**CmAp_LightningDataTableHelper.js**

({
    setPredefinedValues : function(component, helper) {
        let lstData =component.get("v.lstDataRows");
        let lstUnFilteredData = component.get("v.unFilteredData");
       // if($A.util.isUndefined(lstUnFilteredData) || $A.util.isEmpty(lstUnFilteredData)){
            component.set("v.unFilteredData",lstData);
        //}
        this.handelDefaultRowSelection(component, helper);
        component.set("v.showSpinner",false);
    },
    handelDefaultRowSelection: function(component, helper){
        let defaultSelectedRow = component.get("v.defaultSelectedRow");
        if(!$A.util.isUndefined(defaultSelectedRow) && !$A.util.isEmpty(defaultSelectedRow)){
            let temp =[];
            temp.push(defaultSelectedRow);
            let recordTabel = component.find("recordTabel");
            if(recordTabel){
                recordTabel.set("v.selectedRows", temp);            
            }
        }
    },
    searchInRows: function (component, event, helper) {
        let data = component.get("v.lstDataRows"); 
        let allData = component.get("v.unFilteredData"); 
        let strfieldlist = component.get('v.searchString');
        if(!$A.util.isUndefined(strfieldlist) && !$A.util.isEmpty(strfieldlist)){
            let fieldlList = strfieldlist.split(',');
            let searchKey = component.get("v.recordSearchKey");
            if(!$A.util.isUndefined(data) || !$A.util.isEmpty(data)){  
                let filtereddata = allData.filter(checkFieldsValidity);  
                function checkFieldsValidity(word){
                    let isValid=false;
                    for(let fieldname of fieldlList) {
                        if(word.hasOwnProperty(fieldname) && 
                           (!searchKey || word[fieldname].toLowerCase().indexOf(searchKey.toLowerCase()) > -1)){
                            isValid= true;
                            break;
                        }
                    }
                    return isValid;
                }
               component.set("v.lstDataRows", filtereddata); 
                this.handelDefaultRowSelection(component, helper);
            } 
            if(searchKey==''){  
                component.set("v.lstDataRows",component.get("v.unFilteredData"));  
            }
        }

    },
    sortBy: function(field, reverse, primer) {
        var key = primer
        ? function(x) {
            return primer(x[field]);
        }
        : function(x) {
            return x[field];
        };

        return function(a, b) {
            a = key(a);
            b = key(b);
            return reverse * ((a > b) - (b > a));
        };
    },
    handleSort: function(component, event) {debugger;
        var sortedBy = event.getParam('fieldName');
        var sortDirection = event.getParam('sortDirection');
        let data = component.get('v.lstDataRows');
        var cloneData = data.slice(0);
        cloneData.sort((this.sortBy(sortedBy, sortDirection === 'asc' ? 1 : -1)));        
        component.set('v.lstDataRows', cloneData);
        component.set('v.sortDirection', sortDirection);
        component.set('v.sortedBy', sortedBy);
    },
    handelBttonClickAction: function(component, event, helper){
        let compEvent = component.getEvent("onclickActionEvent");
        let tableUniqueName = component.get("v.tableDataType"); 
        compEvent.setParams({"tableUniqueName" : tableUniqueName });
        compEvent.fire();
    },
    handleSelect: function(component, event, helper){
        let compEvent = component.getEvent("onRowChangeEvent");
        let tableUniqueName = component.get("v.tableHeaderTitle"); 
        let selectedRows = event.getParam('selectedRows');
        compEvent.setParams({"tableUniqueName" : tableUniqueName,
                             "selectedRows":selectedRows});
        compEvent.fire();
    },
     rowOnClickActions: function (component, event, helper) {debugger;
        let compEvent = component.getEvent("onClickRowActionEvent");
        let tableUniqueName = component.get("v.tableHeaderTitle"); 
        let objRecord = event.getParam('row');
        let actionName = event.getParam('action').name;
        compEvent.setParams({"tableUniqueName" : tableUniqueName,
                             "selectedRowActionName":actionName,
                             "selectedRowData":objRecord});
        compEvent.fire();
    }
})

How To Use

<c:CmAp_LightningDataTable aura:id="genericTable"
                               tableHeaderTitle ="{!$Label.c.CmAp_DevicesHeader}"
                               panelHeightinPx="350px"
                               headerIcon="standard:planogram"
                               maxRowSelection="1"
                               showRowNumber="{!v.objWrapDevice.showRowNumber}"
                               showHeaderButton="false"
                               enableSearchonRow="{!v.objWrapDevice.enableSearchonRow}"
                               searchString="{!$Label.c.CmAp_DeviceSearchString}"
                               lstDataRows="{!v.objWrapDevice.dataForTable}"
                               lstColumns ="{!v.objWrapDevice.columnForTable}"
                               defaultSelectedRow="{!v.SelectedDeviceId}"/>
Related Topic