Created
August 17, 2019 09:30
-
-
Save vermadhirendra28/7cdec6fa55aaa194206038ab762738f2 to your computer and use it in GitHub Desktop.
Generic Lightning Datatable with New and modify Option
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Desc: A apex class which can be used to create a dynamic lightning datatable only by passing the object name and fields name | |
* that we need to display as column for the Datatable | |
**/ | |
public class CreateDynamicDatatable { | |
/** | |
* Wrapper that will be used by the lightning datatable as columns | |
*/ | |
public with sharing class DynamicDataTableColumn { | |
@AuraEnabled public String type{get; set;} | |
@AuraEnabled public String fieldName{get; set;} | |
@AuraEnabled public String label{get; set;} | |
public DynamicDataTableColumn(String type, String fieldName, String label) { | |
this.type = type; | |
this.fieldName = fieldName; | |
this.label = label; | |
} | |
} | |
/** | |
* A wrapper that represents the collection of column, object name and the list of records data that need | |
* to display in the lightning table | |
**/ | |
public with sharing class DynamicDataTable{ | |
public String strObjectlabel{get;set;} | |
public String strObjectApiName{get;set;} | |
public list<SObject> listObjectData{get;set;} | |
public List<DynamicDataTableColumn> listDataColumn{get;set;} | |
public DynamicDataTable(String p_strObjectlabel,String p_strObjectApiName, | |
list<SObject> p_listObjectData,List<DynamicDataTableColumn> p_listDataColumn){ | |
this.strObjectlabel = p_strObjectlabel; | |
this.strObjectApiName = p_strObjectApiName; | |
this.listObjectData = p_listObjectData; | |
this.listDataColumn = p_listDataColumn; | |
} | |
} | |
/*** | |
* Desc: a method which recieves the object name along with its fields api | |
* and returns the label for that object and its provided fields | |
**/ | |
public static map<String,List<DynamicDataTableColumn>> fetchlabels(map<string,string> p_mapObjectWithField){ | |
map<String,String> mapDataWithlabel = new map<String,String>(); | |
map<String,List<DynamicDataTableColumn>> mapObjectWithColumns = new map<String,List<DynamicDataTableColumn>>(); | |
for(String strObjectName : p_mapObjectWithField.keyset()){ | |
Map<String, Schema.SObjectType> global_describe = Schema.getGlobalDescribe(); | |
Map<String, Schema.SObjectField> object_fields_map = global_describe.get(strObjectName).getDescribe().fields.getMap(); | |
List<DynamicDataTableColumn> columns = new List<DynamicDataTableColumn>(); | |
for(String strFieldApiLabel :p_mapObjectWithField.get(strObjectName).split(',')){ | |
system.debug('datatypevlaue ***'+object_fields_map.get(strFieldApiLabel).getDescribe().getType()); | |
String fieldLabel = object_fields_map.get(strFieldApiLabel).getDescribe().getLabel(); | |
String fieldType = String.valueOf(object_fields_map.get(strFieldApiLabel).getDescribe().getType()); | |
DynamicDataTableColumn newCell = new DynamicDataTableColumn( | |
convertToLightningDataType(fieldType), | |
String.escapeSingleQuotes(strFieldApiLabel), | |
fieldLabel); | |
columns.add(newCell); | |
} | |
mapObjectWithColumns.put(strObjectName,columns); | |
} | |
system.debug('columns***'+mapObjectWithColumns); | |
return mapObjectWithColumns; | |
} | |
/** | |
* Desc: A method that fetch all the data that needs to be displayed on the lightning datatable | |
**/ | |
@AuraEnabled | |
public static map<String,Object> getTableData(String strObjectName, | |
String strApiFields, | |
String strPageRecordSize){ | |
map<String,String> mapObjectWithField = new map<String,String>(); | |
map<String,Object> mapData = new map<String,Object>(); | |
system.debug('strObjectName***'+strObjectName); | |
system.debug('listGridcolumn***'+ strApiFields); | |
mapObjectWithField.put(strObjectName,strApiFields); | |
map<String,List<DynamicDataTableColumn>> mapObjectWithColumns = fetchlabels(mapObjectWithField); | |
system.debug('mapObjectWithColumns***'+mapObjectWithColumns); | |
List<DynamicDataTableColumn> listSelectedGridcol = mapObjectWithColumns.get(strObjectName); | |
list<SObject> listObject = new List<SObject>(); | |
String strQuery = 'Select Id'; | |
for(DynamicDataTableColumn objCell: mapObjectWithColumns.get(strObjectName)){ | |
strQuery = strQuery +','+ objCell.fieldName; | |
} | |
strQuery = strQuery + ' From ' +strObjectName +' limit ' +strPageRecordSize ; | |
system.debug('strQuery***'+strQuery); | |
for(SObject o : Database.query(strQuery)){ | |
listObject.add(o); | |
} | |
system.debug(listObject); | |
map<String,String> mapObjectWithLabel = fetchObjectRelatedLabel(mapObjectWithField); | |
DynamicDataTable objNewData = new DynamicDataTable(mapObjectWithLabel.get(strObjectName), | |
strObjectName, | |
listObject, | |
listSelectedGridcol); | |
mapData.put('DataFetched',JSON.serialize(objNewData)); | |
return mapData; | |
} | |
/** | |
* Desc: A method to fetch the object Label from the api name passed as a parameter to the method | |
**/ | |
public static map<String,String> fetchObjectRelatedLabel(map<String,String> mapSobjectApiWithFields){ | |
map<String,String> mapObjectApiWithLabel = new map<String,String>(); | |
Map <String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe(); | |
for(String objApiName : mapSobjectApiWithFields.keySet()){ | |
String strObjectLabel = String.valueOf(schemaMap.get(objApiName).getDescribe().getLabel()); | |
mapObjectApiWithLabel.put(objApiName,strObjectLabel); | |
} | |
return mapObjectApiWithLabel; | |
} | |
/** | |
* Desc: A method to used to convert the apex datatype to lightning data type | |
**/ | |
public static String convertToLightningDataType(String apexFieldDataType) { | |
String lightningDataType = 'text'; // default | |
apexFieldDataType = apexFieldDataType.toLowerCase(); | |
Map<String, Set<String>> lightningToApexDataTypeMap = new Map<String, Set<String>>{ | |
'text' => new Set<String>{'address', 'id', 'phone', 'email', 'string', 'textarea'}, | |
'date' => new Set<String>{'date', 'datetime'}, | |
'number' => new Set<String>{'double', 'integer'}, | |
'percent' => new Set<String>{'percent'}, | |
'currency' => new Set<String>{'currency'}, | |
'url' => new Set<String>{'url'} | |
}; | |
for (String type : lightningToApexDataTypeMap.keySet()) { | |
if (lightningToApexDataTypeMap.get(type).contains(apexFieldDataType)) { | |
lightningDataType = type; | |
break; | |
} | |
} | |
return lightningDataType; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- lighning component for dynamic data table with new an modify option ----> | |
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" | |
controller="CreateDynamicDatatable"> | |
<aura:attribute name="strObjectApiName" type="String" default="Account" access="global"/> | |
<aura:attribute name="fields" type="String" | |
default="Name" access="global"/> | |
<aura:attribute name="fetchedSelectedRecords" type="Object" access="global"/> | |
<aura:attribute name="selectedObjectRecList" type="list" /> | |
<aura:attribute name="noOfRecordsOnPage" type="integer" default="100" access="global"/> | |
<aura:attribute name="displayTable" type="boolean" default="true" /> | |
<aura:attribute name="Spinner" type="boolean" default="false"/> | |
<aura:attribute name="displayCreateModalForm" type="boolean" default="false"/> | |
<aura:attribute name="displayModifyModal" type="boolean" default="false"/> | |
<aura:attribute name="objFields" type="String[]"></aura:attribute> | |
<aura:attribute name="modifiedRecordId" type="String" default=""/> | |
<aura:attribute name="modifiedbuttondisabled" type="Boolean" default="false" /> | |
<aura:attribute name="modifylabel" type="string" default="Modify" access="global"/> | |
<aura:attribute name="isEditable" type="boolean" default="true" access="global" /> | |
<lightning:notificationsLibrary aura:id="notifLib"/> | |
<!--aura:attrbute name="strObjectLabel" type="String" access="global"/ --> | |
<!--handler method --> | |
<aura:handler event="aura:waiting" action="{!c.showSpinner}"/> | |
<aura:handler event="aura:doneWaiting" action="{!c.hideSpinner}"/> | |
<aura:handler name="init" value="{!this}" action="{!c.init}" /> | |
<!-- Register events --> | |
<aura:registerEvent name="callPassDynamicSelectedRecEvt" type="c:passDynamicSelectedRec"/> | |
<div class="container"> | |
<!--loading spinner start... style=Brand Medium (blue dots)--> | |
<aura:if isTrue="{!v.Spinner}"> | |
<div aura:id="spinnerId" class="slds-spinner_container"> | |
<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert"> | |
<span class="slds-assistive-text">Loading</span> | |
<div class="slds-spinner__dot-a"></div> | |
<div class="slds-spinner__dot-b"></div> | |
</div> | |
</div> | |
</aura:if> | |
<aura:if isTrue="{!v.displayTable}"> | |
<div class="slds-page-header"> | |
<article class="slds-card"> | |
<div class="slds-card__header slds-grid"> | |
<header class="slds-media slds-media_center slds-has-flexi-truncate"> | |
<div class="slds-media__figure"> | |
<span class="slds-icon_container slds-icon-standard-account" title="{!v.fetchedSelectedRecords.strObjectlabel}"> | |
<lightning:icon iconName="utility:custom1" size="medium"/> | |
<span class="slds-assistive-text">{!v.fetchedSelectedRecords.strObjectlabel}</span> | |
</span> | |
</div> | |
<div class="slds-media__body"> | |
<h2 class="slds-card__header-title"> | |
<a href="javascript:void(0);" class="slds-card__header-link slds-truncate" title="Accounts"> | |
<span>{!v.fetchedSelectedRecords.strObjectlabel}</span> | |
</a> | |
</h2> | |
</div> | |
<div class="slds-no-flex"> | |
<button class="slds-button slds-button_neutral" data-record="{!v.fetchedSelectedRecords.strObjectApiName}" | |
onclick="{!c.createNewRecord}" | |
data-label="New">New</button> | |
</div> | |
</header> | |
</div> | |
<div class="slds-card__body slds-card__body_inner"> | |
<lightning:datatable data="{!v.fetchedSelectedRecords.listObjectData}" | |
columns="{!v.fetchedSelectedRecords.listDataColumn}" | |
keyField="Id" | |
onrowselection="{!c.handleSelect}" | |
onrowaction="{!c.modifySelectedRecord}"/> | |
</div> | |
</article> | |
<div style="padding-top:2%;text-align:center"> | |
<button class="slds-button slds-button_brand" onclick="{!c.getSelectedRec}"> | |
Get Seleced Records | |
</button> | |
</div> | |
</div> | |
</aura:if> | |
<!--modal popup to display the editable form for the selected record--> | |
<aura:if isTrue ="{!v.displayModifyModal}"> | |
<div style="height:640px"> | |
<section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" | |
aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open"> | |
<div class="slds-modal__container"> | |
<header class="slds-modal__header"> | |
<button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" | |
title="Close" onclick="{!c.closeModifyModal}"> | |
<lightning:icon iconName="action:close" size="small"/> | |
<span class="slds-assistive-text">Close</span> | |
</button> | |
<h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate"> | |
{!v.fetchedSelectedRecords.strObjectlabel} | |
</h2> | |
</header> | |
<div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1"> | |
<lightning:recordEditForm | |
onload="{!c.handleModifyLoad}" | |
onsubmit="{!c.handleModifySubmit}" | |
onsuccess="{!c.handleModifySuccess}" | |
recordId="{!v.modifiedRecordId}" | |
objectApiName="{!v.fetchedSelectedRecords.strObjectApiName}"> | |
<!-- the messages component is for error messages --> | |
<lightning:messages /> | |
<aura:iteration items="{!v.objFields}" var="fieldApiname"> | |
<lightning:inputField fieldName="{!fieldApiname}" /> | |
</aura:iteration> | |
<div class="slds-m-top_medium"> | |
<lightning:button disabled="{!v.modifiedbuttondisabled}" variant="brand" type="submit" name="save" label="Save" /> | |
</div> | |
</lightning:recordEditForm> | |
</div> | |
<footer class="slds-modal__footer"> | |
</footer> | |
</div> | |
</section> | |
<div class="slds-backdrop slds-backdrop_open"></div> | |
</div> | |
</aura:if> | |
<!--end of modal popup for editable form --> | |
<!-- beginning of create record modal --> | |
<aura:if isTrue="{!v.displayCreateModalForm}"> | |
<div style="height:640px"> | |
<section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" | |
aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open"> | |
<div class="slds-modal__container"> | |
<header class="slds-modal__header"> | |
<button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" | |
title="Close" onclick="{!c.closeCreateFormModal}"> | |
<lightning:icon iconName="action:close" size="small"/> | |
<span class="slds-assistive-text">Close</span> | |
</button> | |
<h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate"> | |
{!v.fetchedSelectedRecords.strObjectlabel} | |
</h2> | |
</header> | |
<div class="slds-modal__content slds-p-around_medium" id="modal-content-id-3"> | |
<lightning:recordForm | |
objectApiName="{!v.strObjectApiName}" | |
fields="{!v.objFields}" | |
onsuccess="{!c.handleSuccess}" /> | |
</div> | |
<footer class="slds-modal__footer"> | |
</footer> | |
</div> | |
</section> | |
<div class="slds-backdrop slds-backdrop_open"></div> | |
</div> | |
</aura:if> | |
<aura:if isTrue ="{!!displayTable}"> | |
<div class="messagecontainer center fontsize"> | |
</div> | |
</aura:if> | |
</div> | |
</aura:component> | |
<!--- end of lightning component --> | |
/**Begining of dynamicLDTWithNewAndModifyOptionController.js code **/ | |
({ | |
init : function(component, event, helper) { | |
helper.getTableData(component,event); | |
}, | |
handleSelect:function(component,event,helper){ | |
try{ | |
helper.createSelectedReclist(component,event); | |
}catch(e){ | |
console.error(e); | |
} | |
}, | |
getSelectedRec: function(component,event,helper){ | |
try{ | |
helper.fetchSelectedRecordList(component,event); | |
}catch(e){ | |
console.error(e); | |
} | |
}, | |
createNewRecord: function(component,event,helper){ | |
helper.displayRecordCreateForm(component,event); | |
}, | |
closeCreateFormModal: function(component,event,helper){ | |
component.set("v.displayCreateModalForm",false); | |
}, | |
modifySelectedRecord : function(component,event,helper){ | |
helper.displayModifyForm(component,event); | |
}, | |
closeModifyModal : function(component,event,helper){ | |
component.set("v.displayModifyModal",false); | |
}, | |
handleModifyLoad: function(cmp, event, helper) { | |
}, | |
handleModifySubmit: function(cmp, event, helper) { | |
cmp.set('v.modifiedbuttondisabled', true); | |
}, | |
handleModifySuccess: function(component, event, helper) { | |
// errors are handled by lightning:inputField and lightning:nessages | |
// so this just hides the spinnet | |
component.set("v.displayModifyModal",false); | |
alert('record successfully modified'); | |
}, | |
closeCreateFormModal : function(component,event,helper){ | |
component.set("v.displayModifyModal",false); | |
}, | |
handleSuccess : function(component, event, helper) { | |
component.set("v.displayCreateModalForm",false); | |
var recId = event.getParam("id"); | |
console.log(recId); | |
if(recId != undefined){ | |
alert('record successfully inserted'); | |
}else{ | |
alert('some error occured during record insertion'); | |
} | |
}, | |
// this function automatic call by aura:waiting event | |
showSpinner: function(component, event, helper) { | |
// make Spinner attribute true for display loading spinner | |
component.set("v.Spinner", true); | |
}, | |
// this function automatic call by aura:doneWaiting event | |
hideSpinner : function(component,event,helper){ | |
// make Spinner attribute to false for hide loading spinner | |
component.set("v.Spinner", false); | |
} | |
}) | |
/** end of dynamicLDTWithNewAndModifyOptionController.js code **/ | |
/** | |
* Begining of dynamicLDTWithNewAndModifyOptionHelper.js code ** | |
*/ | |
({ | |
getTableData : function(cmp,event) { | |
var limitRecordsize = cmp.get("v.noOfRecordsOnPage"); | |
var execAction = cmp.get("c.getTableData"); | |
execAction.setParams({ | |
"strObjectName":cmp.get("v.strObjectApiName"), | |
"strApiFields" :cmp.get("v.fields"), | |
"strPageRecordSize":limitRecordsize | |
}); | |
this.serverSideCall(cmp,execAction).then( | |
function(res) { | |
console.log(res); | |
var queriedResult = JSON.parse(res.DataFetched); | |
if(cmp.get("v.isEditable")){ | |
queriedResult.listDataColumn.push({type: "button", | |
typeAttributes: { | |
label: cmp.get("v.modifylabel"), | |
name: cmp.get("v.strObjectApiName"), | |
title: cmp.get("v.modifylabel"), | |
disabled: false, | |
value: cmp.get("v.modifylabel"), | |
iconPosition: 'left' | |
} | |
}); | |
} | |
if(queriedResult.listObjectData.length > 0){ | |
cmp.set("v.fetchedSelectedRecords",queriedResult); | |
console.log('listObjectData***'+queriedResult.listObjectData); | |
/*Data.forEach(function assignModifyColumn(temp,index){ | |
console.log('form column data'+temp);*/ | |
//cmp.set("v.strObjectLabel",queriedResult.strObjectlabel); | |
} | |
}).catch( | |
function(error) { | |
console.log(error); | |
cmp.set("v.displayTable",false); | |
} | |
); | |
}, | |
createSelectedReclist: function(component,event){ | |
var selectedRows = event.getParam('selectedRows'); | |
let listObjectRec = [] ; | |
if(selectedRows.length > 0){ | |
for(var i = 0;i<selectedRows.length;i++){ | |
listObjectRec.push(selectedRows[i].Id); | |
} | |
} | |
console.log('list of selectedrec ***'+listObjectRec); | |
component.set('v.selectedObjectRecList',listObjectRec); | |
}, | |
/** | |
* A javascript method to pass the selected recordlist back to the parent component | |
**/ | |
fetchSelectedRecordList: function(component,event){ | |
var selectedReclist = component.get("v.selectedObjectRecList"); | |
var cmpEvent = component.getEvent("callPassDynamicSelectedRecEvt"); | |
cmpEvent.setParams({"selectedRecordId" : selectedReclist}); | |
cmpEvent.fire(); | |
}, | |
/** | |
* a method to create the new record for the selected object | |
**/ | |
displayRecordCreateForm: function(component,event){ | |
var objectFields = component.get("v.fields"); | |
var listSelectedObjectFields = objectFields.split(','); | |
component.set("v.objFields",listSelectedObjectFields); | |
component.set("v.displayCreateModalForm",true); | |
}, | |
/** | |
* A method to modify the existing records | |
**/ | |
displayModifyForm : function(component,event){ | |
var recId = event.getParam('row').Id; | |
var actionObjectName = event.getParam('action').name; | |
console.log(actionObjectName); | |
console.log(recId); | |
var objectFields = component.get("v.fields"); | |
var listSelectedObjectFields = objectFields.split(','); | |
component.set("v.objFields",listSelectedObjectFields); | |
component.set("v.modifiedRecordId",recId); | |
component.set("v.displayModifyModal",true); | |
}, | |
/** | |
* Inclusion of Promise then to handle the response recieved from controller in a more appropriate way | |
**/ | |
serverSideCall : function(component,action) { | |
return new Promise(function(resolve, reject) { | |
action.setCallback(this, | |
function(response) { | |
var state = response.getState(); | |
if (state === "SUCCESS") { | |
var result = response.getReturnValue(); | |
resolve(result); | |
} else { | |
reject(new Error(response.getError())); | |
} | |
}); | |
$A.enqueueAction(action); | |
}); | |
}, | |
}) | |
/** end of helper code **/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<aura:event type="COMPONENT" description="Event template" > | |
<aura:attribute name="selectedRecordId" type="list" /> | |
</aura:event> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- code for lightning Datatable With new and modify Option ---> | |
<!-- parent component which will be using the dynamic lightning datatable --> | |
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes, | |
flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes, | |
force:lightningQuickAction" access="global" > | |
<aura:attribute name="strObjectAPIName" type="String" default="Account"/> | |
<aura:attribute name="strFieldsAPIName" type="String" default="Name,Phone" /> | |
<aura:attribute name="listSelectedObjectRecId" type="list" /> | |
<aura:attribute name="noOfRecLimit" type="integer" default="5"/> | |
<aura:attribute name="displaySelectedRecid" type="boolean" default="false"/> | |
<aura:attribute name="isEditable" type="boolean" default="true"/> | |
<!--handle the event fired from the child components --> | |
<aura:handler name="callPassDynamicSelectedRecEvt" event="c:passDynamicSelectedRec" action="{!c.handleSelectedRecordEvent}"/> | |
<div class="container"> | |
<c:dynamicLightningDataTable strObjectApiName="{!v.strObjectAPIName}" | |
fields="{!v.strFieldsAPIName}" noOfRecordsOnPage="{!v.noOfRecLimit}" | |
modifylabel="Edit" isEditable="{!v.isEditable}"/> | |
<aura:if isTrue="{!v.displaySelectedRecid}"> | |
<aura:iteration items="{!v.listSelectedObjectRecId}" var="RecId"> | |
Selected RecordIds : {!RecId}<br/> | |
</aura:iteration> | |
</aura:if> | |
</div> | |
</aura:component> | |
<!--End of lighnting.cmp ode that user dynamic datatable --> | |
/** | |
* code for its lightning controller that gives us the list of the selected record from the dynamic LIghtning datatable | |
**/ | |
({ | |
handleSelectedRecordEvent : function(component, event, helper) { | |
var selectedObjectReclist = event.getParam("selectedRecordId"); | |
// set the handler attributes based on event data | |
component.set("v.listSelectedObjectRecId", selectedObjectReclist); | |
component.set("v.displaySelectedRecid",true); | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment