Pages

Wednesday, 4 September 2019

Datatable with Export, Edit, Delete, new and export operations

Apex Classes -
    1. DatatableContrl
   
Lightning Events -
    1. InputFormCloseEvent
    2. SearchEvent
   
Lightning Components -
    1. Message.cmp
    2. SearchDatatable.cmp
    3. Pagination
    4. InputForm4MulRecs
    5. DatatableV2

Main Component -
    DatatableV2.cmp

Apex Classes -
public with sharing class DatatableContrl {
/*@AuraEnabled
public static List<Opportunity> getRecords() {
return [select Name, StageName, Amount, CloseDate from Opportunity Limit : Limits.getLimitQueryRows()];
}
@AuraEnabled
public static List<String> getFields() {
return null;
}*/
@AuraEnabled
public static Map<String,Object> initData(Map<String,Object> inputMap) {
String objectName = (String)inputMap.get('objectName'),
fieldSetName = (String)inputMap.get('fieldSetName'),
filterVal = (String)inputMap.get('filterVal');
Map<String,Object> contrlRes = new Map<String,Object>{
'records' => null,
'fields' => null
};
List<FieldInfo> fields = Utility_v1.getFieldSetFields(objectName, fieldSetName);
system.debug('***fields: '+fields);
contrlRes.put('fields',fields);
String fieldStr = Utility_v1.getFieldsStr(fields);
system.debug('***fieldStr: '+fieldStr);
String query = 'select '+fieldStr+' from '+objectName;
if(String.isNotBlank(filterVal)) {
String finalFilter = ' Where Name like \'%'+filterVal+'%\'';
query += finalFilter;
}
query += ' Limit '+Limits.getLimitQueryRows();
system.debug('***query: '+query);
contrlRes.put('records',Database.query(query));
return contrlRes;
}
@AuraEnabled
public static String save(List<Sobject> records) {
String msg = '';
Schema.SObjectType sObjectType = records[0].getSObjectType();
system.debug('sObjectType: '+sObjectType);
if (sObjectType != null) {
String listType = 'List<' + sObjectType + '>';
List<SObject> castRecords = (List<SObject>)Type.forName(listType).newInstance();
castRecords.addAll(records);
try {
upsert castRecords;
msg = 'Records have been saved successfully.';
}
catch(Exception e) {
msg = 'Error:' + e.getMessage();
}
}
return msg;
}
@AuraEnabled
public static Map<String,Object> options() {
Map<String,Object> contrlRes = new Map<String,Object>();
List<selOption> options = new List<selOption>();
Schema.DescribeFieldResult fieldResult = Opportunity.StageName.getDescribe();
List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
for( Schema.PicklistEntry f : ple) {
options.add(new selOption(f.getLabel(), f.getValue()));
}
contrlRes.put('stage',options);
system.debug('contrlRes: '+contrlRes);
return contrlRes;
}
@AuraEnabled
public static String saveRecs(String opps) {
String msg = '';
try {
insert (List<Opportunity>)Utility_v1.convertJsonToSobjectLst(opps);
msg = 'Records have been saved successfully.';
}
catch(Exception e) {
msg = system.today()+'Error: '+e.getMessage();
}
system.debug('**msg: '+msg);
return msg;
}
@AuraEnabled
public static String deleteRecs(List<Opportunity> recs) {
system.debug('**del: '+recs);
String msg = '';
try {
delete recs;
msg = 'Records have been deleted successfully.';
}
catch(Exception e) {
msg = system.today()+'Error: '+e.getMessage();
}
system.debug('**msg: '+msg);
return msg;
}
}
view raw DatatableContrl hosted with ❤ by GitHub
Lightning Events -
<aura:event type="COMPONENT" description="Fire on click on close or ok in InputForm Comp." />
<aura:event type="APPLICATION" description="Event template" >
<aura:attribute name="searchVal" type="String"/>
</aura:event>
view raw SearchEvent hosted with ❤ by GitHub
Lightning Components -
UI -
<aura:component >
<aura:attribute name="msg" type="String" default="Error"/>
<aura:attribute name="msgType" type="String" default="success"/>
<div id="responseErrors" class="{!'slds-notify slds-notify--alert slds-theme--'+v.msgType+' slds-theme--inverse-text slds-theme--alert-texture'}" role="alert">
<span class="slds-assistive-text">Info</span>
<lightning:buttonIcon iconName="utility:close" alternativeText="close" class="slds-button slds-button--icon-inverse slds-notify__close" onclick="{!c.close}"/>
<h2>{!v.msg}</h2>
</div>
</aura:component>
Controller Js -
({
close : function(cmp, event, helper) {
cmp.set("v.msg",'');
cmp.set("v.msgType",'');
}
})
view raw Message hosted with ❤ by GitHub
UI -
<aura:component>
<aura:attribute name="searchVal" type="String"/>
<aura:registerEvent name="appEvent" type="c:SearchEvent"/>
<form class="slds-form--inline">
<div class="slds-form-element">
<div class="slds-form-element__control">
<lightning:input id="source" placeholder="Enter a Name" type="text" value="{!v.searchVal}"/>
</div>
</div>
<div class="slds-form-element">
<button class="slds-button slds-button--brand" onclick="{!c.search}" type="button">Search</button>
</div>
</form>
</aura:component>
Controller Js -
({
search : function(cmp, event, helper) {
var appEvent = $A.get("e.c:SearchEvent");
debugger;
appEvent.setParams({
"searchVal" : cmp.get("v.searchVal")
});
appEvent.fire();
}
})
view raw SearchDatatable hosted with ❤ by GitHub
UI -
<aura:component >
<aura:attribute name="pageNumber" type="Integer" default="1"/>
<aura:attribute name="lastPageNumber" type="Integer"/>
<div class="slds-button-group" role="group">
<button class="slds-button slds-button_neutral" onclick="{!c.onButtonClick}" disabled="{!v.pageNumber == 1}">First</button>
<button class="slds-button slds-button_neutral" onclick="{!c.onButtonClick}" disabled="{!v.pageNumber == 1}">Previous</button>
<button class="slds-button slds-button_neutral" onclick="{!c.onButtonClick}" disabled="{!v.pageNumber == v.lastPageNumber}">Next</button>
<lightning:button label="Last" title="Last" onclick="{! c.onButtonClick}" disabled="{!v.pageNumber == v.lastPageNumber}"/>
<div>Page: {!v.pageNumber} of {!v.lastPageNumber}</div>
</div>
</aura:component>
Controller -
({
onButtonClick : function(cmp, event, helper) {
debugger;
//console.log('Button Name: '+event.target.innerHTML);
var buttonType;
if(event.getSource)
buttonType = event.getSource().get("v.label");
else
buttonType = event.target.innerHTML;
console.log('Button Name: '+buttonType);
if(buttonType == 'First') {
cmp.set("v.pageNumber",1);
}
else if(buttonType == 'Previous') {
cmp.set("v.pageNumber",cmp.get("v.pageNumber")-1);
}
else if(buttonType == 'Next') {
cmp.set("v.pageNumber",cmp.get("v.pageNumber")+1);
}
else if(buttonType == 'Last') {
cmp.set("v.pageNumber",cmp.get("v.lastPageNumber"));
}
console.log('page number: '+cmp.get("v.pageNumber"));
}
})
view raw Pagination hosted with ❤ by GitHub
UI -
<aura:component controller="DatatableContrl">
<aura:attribute name="selectedRows" type="List"/>
<aura:attribute name="objectName" type="String"/>
<aura:attribute name="fields" type="List" default=""/>
<aura:attribute name="isEdit" type="Boolean"/>
<aura:attribute name="rowsToUpd" type="List"/>
<aura:attribute name="msg" type="String"/>
<aura:attribute name="msgType" type="String"/>
<aura:registerEvent name="inClsEnt" type="c:InputFormCloseEvent"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<div class="demo-only" 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">
<div class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse">
<lightning:buttonIcon iconName="utility:close" variant="bare-inverse" alternativeText="close" onclick="{!c.onClose}"/>
</div>
<h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Input Form</h2>
</header>
<aura:if isTrue="{!v.msg}">
<c:Message msg="{!v.msg}" msgType="{!v.msgType}"/>
</aura:if>
<div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
<table class="slds-table slds-table_bordered slds-table_cell-buffer">
<thead>
<tr class="slds-text-title_caps">
<aura:iteration items="{!v.fields}" var="fld">
<th scope="col">
<div class="slds-truncate" title="{!fld.label}">{!fld.label}</div>
</th>
</aura:iteration>
</tr>
</thead>
<tbody>
<aura:iteration items="{!v.selectedRows}" var="row" indexVar="index">
<tr id="{!index}">
<aura:iteration items="{!row.entries}" var="fldVal">
<td data-label="{!fldVal.value}">
<div class="slds-form-element">
<div class="slds-form-element__control">
<input type="text" id="{!fldVal.fieldName}" class="slds-input" placeholder="{!fldVal.type}" value="{!fldVal.value}"/>
</div>
</div>
</td>
</aura:iteration>
</tr>
</aura:iteration>
</tbody>
</table>
</div>
<footer class="slds-modal__footer">
<button class="slds-button slds-button_neutral" disabled="{!!v.msg}" onclick="{!c.onClose}">Ok</button>
<button class="slds-button slds-button_brand" onclick="{!c.onSave}">Save</button>
</footer>
</div>
</section>
<div class="slds-backdrop slds-backdrop_open"></div>
</div>
</aura:component>
Controller Js -
({
doInit : function(cmp, event, helper) {
var records = cmp.get("v.selectedRows");
var fields = cmp.get("v.fields");
for(var i = 0; i < records.length; i++) {
records[i].entries = [];
for(var j = 0; j < fields.length; j++) {
records[i].entries.push({
label : fields[j].label,
fieldName : fields[j].fieldName,
type : fields[j].type,
value : records[i][fields[j].fieldName],
});
}
}
console.log('records: '+JSON.stringify(records));
cmp.set("v.selectedRows",records);
},
onClose : function(cmp, event, helper) {
cmp.set("v.msg",'');
cmp.set("v.msgType",'');
cmp.set("v.isEdit",false);
var compEvent = cmp.getEvent("inClsEnt");
// Optional: set some data for the event (also known as event shape)
// A parameter’s name must match the name attribute
// of one of the event’s <aura:attribute> tags
// compEvent.setParams({"myParam" : myValue });
compEvent.fire();
},
onSave : function(cmp, event, helper) {
//var input = document.getElementById("0").getElementsByTagName("td")[0].getElementsByClassName("slds-input")[0];
//alert(input.type);
debugger;
var records = cmp.get("v.selectedRows");
var recSize = records.length;
var recs = [];
for(var i=0;i< recSize; i++) {
var tds = document.getElementById(i).getElementsByTagName("td");
var json = {};
json['Id'] = records[i].Id;
json['sObjectType'] = cmp.get("v.objectName");
for(var j=0;j<tds.length;j++) {
var type = tds[j].getElementsByClassName("slds-input")[0].placeholder;
//console.log(type);
var val = tds[j].getElementsByClassName("slds-input")[0].value;
json[tds[j].getElementsByClassName("slds-input")[0].id] = (type == 'double' || type == 'currency') ? parseInt(val) : val;
}
console.log('*** json:** '+JSON.stringify(json));
recs.push(json);
}
//console.log('### recs: '+JSON.stringify(recs));
cmp.set("v.rowsToUpd",recs);
helper.handleOnSave(cmp, event);
}
})
Helper Js -
({
handleOnSave : function(cmp, event) {
debugger;
var records = cmp.get("v.rowsToUpd");
console.log(JSON.stringify(records));
var action = cmp.get("c.save");
action.setParams({
records : records
});
action.setCallback(this,function(response) {
var state = response.getState();
if(state == 'SUCCESS') {
var res = response.getReturnValue();
cmp.set("v.msg",res);
if(res.startsWith('Error'))
cmp.set("v.msgType",'error');
else
cmp.set("v.msgType",'success');
}
else {
cmp.set("v.msg",'Error.');
cmp.set("v.msgType",'error');
}
});
$A.enqueueAction(action);
}
})
UI -
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction,lightning:actionOverride" access="global" controller="DatatableContrl">
<aura:attribute name="records" type="Object" default=""/>
<aura:attribute name="fields" type="List" default=""/>
<aura:attribute name="objectName" type="String" default="Opportunity"/>
<aura:attribute name="fieldSetName" type="String" default="ReadableFields"/>
<aura:attribute name="selectedRows" type="List" default=""/>
<aura:attribute name="isEdit" type="Boolean" default="false"/>
<!-- Pagination -->
<aura:attribute name="currentRecords" type="Object" default=""/>
<aura:attribute name="pNumber" type="Integer" default="1"/>
<aura:attribute name="lastPageNumber" type="Integer"/>
<aura:attribute name="limitSize" type="Integer" default="5"/>
<aura:attribute name="msg" type="String"/>
<aura:attribute name="msgType" type="String"/>
<aura:attribute name="showSpinner" type="Boolean" default="false"/>
<aura:attribute name="spinnerType" type="String" default="default"/>
<aura:attribute name="searchVal" type="String"/>
<aura:handler name="change" value="{!v.pNumber}" action="{!c.getSubset}"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:handler name="inClsEnt" event="c:InputFormCloseEvent" action="{!c.doInit}"/>
<aura:handler name="recsClsEnt" event="c:InputFormCloseEvent" action="{!c.close}"/>
<aura:handler event="c:SearchEvent" action="{!c.handleApplicationEvent}"/>
<div class="slds-section slds-is-open">
<h3 class="slds-section__title slds-theme_success">
<span class="slds-truncate slds-p-horizontal_small" title="Section Title">
Datatable Example with the actions at table level (Works on selected bulk records)
</span>
</h3>
<div aria-hidden="false" class="slds-section__content">
<p></p>
</div>
</div>
<aura:if isTrue="{!v.msg}">
<c:Message msg="{!v.msg}" msgType="{!v.msgType}"/>
</aura:if>
<aura:if isTrue="{!v.showSpinner}">
<aura:if isTrue="{!v.spinnerType == 'default'}">
<lightning:spinner alternativeText="Loading" size="small" />
<aura:set attribute="else">
<ui:spinner />
</aura:set>
</aura:if>
</aura:if>
<!-- <div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/V3FW3NsG4J4" frameborder="0" allow="autoplay; encrypted-media">
</iframe>
</div> -->
<div class="slds-button-group" role="group">
<div style="padding: 0.5rem; background-color: rgb(244, 246, 249);">
<lightning:buttonIcon iconName="utility:new" alternativeText="new" onclick="{!c.new}"/>
</div>
<div style="padding: 0.5rem; background-color: rgb(244, 246, 249);">
<lightning:buttonIcon iconName="utility:edit" alternativeText="edit" onclick="{!c.edit}"/>
</div>
<div style="padding: 0.5rem; background-color: rgb(244, 246, 249);">
<lightning:buttonIcon iconName="utility:delete" alternativeText="delete" onclick="{!c.delete}"/>
</div>
<div style="padding: 0.5rem; background-color: rgb(244, 246, 249);">
<lightning:buttonIcon iconName="utility:download" alternativeText="download" onclick="{!c.export}"/>
</div>
<div>
<c:SearchDatatable/>
</div>
</div>
<div style="height: 250px;" class="tableColor">
<lightning:datatable
keyField="id"
columns="{! v.fields}"
data="{! v.currentRecords}"
hideCheckboxColumn="false"
onrowselection = "{!c.getSelRows}"/>
</div><br/>
<!--
# - one way data binding
! - two way data binding
-->
<c:Pagination pageNumber="{!v.pNumber}" lastPageNumber="{!v.lastPageNumber}"/><br/><br/><br/><br/>
<aura:if isTrue="{!and(v.isEdit, !v.msg)}">
<c:InputForm4MulRecs selectedRows="{!v.selectedRows}" fields="{!v.fields}" isEdit="{!v.isEdit}" objectName="{!v.objectName}"/>
</aura:if>
{!v.body}
<c:FooterComponent />
</aura:component>
Controller Js -
({
doInit : function(component, event, helper) {
helper.handleDoInit(component, event);
},
getSubset : function(component, event, helper) {
helper.handleSubset(component, event);
},
getSelRows : function(component, event, helper) {
helper.handleGetSelRows(component, event);
},
export : function(component, event, helper) {
component.set("v.msg",'');
component.set("v.msgType",'');
helper.handleExport(component, event);
},
edit : function(cmp, event, helper) {
cmp.set("v.isEdit",true);
cmp.set("v.msg",'');
cmp.set("v.msgType",'');
helper.handleEdit(cmp, event);
},
delete : function(cmp, event, helper) {
var isTrue = confirm("Are you sure you want to delete?");
if(isTrue) {
cmp.set("v.msg",'');
cmp.set("v.msgType",'');
alert('You are not authorized to delete');
//helper.handleDelete(cmp, event);
}
},
new : function(cmp, event, helper) {
$A.createComponent(
"c:RecordsCreationComp",
{},
function(newComp, status, errorMessage){
//Add the new button to the body array
if (status === "SUCCESS") {
var body = cmp.get("v.body");
body.push(newComp);
cmp.set("v.body", body);
}
else if (status === "INCOMPLETE") {
console.log("No response from server or client is offline.")
// Show offline error
}
else if (status === "ERROR") {
console.log("Error: " + errorMessage);
// Show error message
}
}
);
},
close : function(cmp, event, helper) {
debugger;
cmp.set("v.body", '');
},
handleApplicationEvent: function(cmp, event, helper) {
var message = event.getParam("searchVal");
console.log('**msg'+message);
cmp.set("v.searchVal",message);
helper.handleDoInit(cmp, event);
}
})
Helper Js -
({
handleDoInit : function(cmp,event) {
cmp.set("v.spinnerType","other");
cmp.set("v.showSpinner",true);
var action = cmp.get("c.initData");
var inputMap = {
'objectName' : cmp.get("v.objectName"),
'fieldSetName' : cmp.get("v.fieldSetName"),
'filterVal' : cmp.get("v.searchVal")
};
action.setParams({
inputMap : inputMap
});
action.setCallback(this,function(res){
var state = res.getState();
if(state == 'SUCCESS') {
var response = res.getReturnValue();
console.log('**response.records: '+JSON.stringify(response.records));
console.log('**response.fields: '+JSON.stringify(response.fields));
cmp.set("v.records",response.records);
cmp.set("v.lastPageNumber",Math.ceil(cmp.get("v.records").length/cmp.get("v.limitSize")));
cmp.set("v.fields",response.fields);
this.handleSubset(cmp,event);
}
cmp.set("v.showSpinner",false);
});
$A.enqueueAction(action);
},
handleSubset : function(cmp,event) {
var records = cmp.get("v.records");
var limitSize = cmp.get("v.limitSize");
var lastPageNumber = cmp.get("v.lastPageNumber");
var pNumber = cmp.get("v.pNumber");
var currentRecords = records.slice((pNumber -1) * limitSize,limitSize * pNumber);
cmp.set("v.currentRecords",currentRecords);
},
handleGetSelRows : function(cmp,event) {
cmp.set("v.selectedRows",event.getParam('selectedRows'));
console.log('***sel rows: '+JSON.stringify(event.getParam('selectedRows')));
},
handleEdit : function(cmp,event) {
var records = cmp.get("v.selectedRows");
if(records.length > 0) {
}
else {
cmp.set("v.msg",$A.get("$Label.c.DatatableNoSelMsg"));
cmp.set("v.msgType",'error');
}
},
handleDelete : function(cmp,event) {
debugger;
var records = cmp.get("v.selectedRows");
var action = cmp.get("c.deleteRecs");
action.setParams({
recs : records
});
action.setCallback(this,function(res){
var state = res.getState();
if(state == 'SUCCESS') {
var response = res.getReturnValue();
console.log('**response.records: '+JSON.stringify(response));
this.handleDoInit(cmp,event);
}
cmp.set("v.showSpinner",false);
});
$A.enqueueAction(action);
},
handleExport : function(cmp,event) {
var records = cmp.get("v.selectedRows");
if(records.length > 0) {
debugger;
// call the helper function which "return" the CSV data as a String
var csv = this.convertArrayOfObjectsToCSV(cmp,records,cmp.get("v.fields"));
if (csv == null){return;}
// ####--code for create a temp. <a> html tag [link tag] for download the CSV file--####
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv);
hiddenElement.target = '_self'; //
hiddenElement.download = 'ExportData.csv'; // CSV file Name* you can change it.[only name not .csv]
document.body.appendChild(hiddenElement); // Required for FireFox browser
hiddenElement.click(); // using click() js function to download csv file
}
else {
cmp.set("v.msg",$A.get("$Label.c.DatatableNoSelMsg"));
cmp.set("v.msgType",'error');
}
},
convertArrayOfObjectsToCSV : function(cmp,objectRecords, fields){
debugger;
// declare variables
var csvStringResult, counter, keys = [], columnDivider, lineDivider;
// check if "objectRecords" parameter is null, then return from function
if (objectRecords == null || !objectRecords.length) {
return null;
}
// store ,[comma] in columnDivider variabel for sparate CSV values and
// for start next line use '\n' [new line] in lineDivider varaible
columnDivider = ',';
lineDivider = '\n';
// in the keys valirable store fields API Names as a key
// this labels use in CSV file header
for(var i=0; i < fields.length; i++) {
keys.push(fields[i].fieldName);
}
csvStringResult = '';
csvStringResult += keys.join(columnDivider);
csvStringResult += lineDivider;
for(var i=0; i < objectRecords.length; i++){
counter = 0;
for(var sTempkey in keys) {
var skey = keys[sTempkey] ;
// add , [comma] after every String value,. [except first]
if(counter > 0){
csvStringResult += columnDivider;
}
csvStringResult += '"'+ objectRecords[i][skey]+'"';
counter++;
} // inner for loop close
csvStringResult += lineDivider;
}// outer main for loop close
// return the CSV formate String
return csvStringResult;
}
})
view raw DatatableV2 hosted with ❤ by GitHub

Live Demo -

DatatableV2 Live Example

1 comment: