Introduction:
If we need to view some of data based on some criteria from an object by clicking a Button, we can use this custom component. The component is dynamic and works for any object in Salesforce without modifying the component.
Component: [Parent Component]
<aura:component implements = “force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction,force:hasSObjectName”
access = “global”
controller = “DynamicClass” >
<!– Attributes used in this component –>
<aura:attribute name = “sobjecttype” type = “String” default = “{!v.sObjectName}”
<aura:attribute name = “sobjectField” type = “List” default = “[]” />
<aura:attribute name = “sobject” type = “List” default = “[]” />
<aura:attribute name = “selectedObject” type = “List” default = “[]” />
<aura:attribute name = “isOpenChooseBox” type = “boolean” default = “false” />
<aura:attribute name = “isOpenFieldBox” type = “boolean” default = “false” />
<aura:attribute name = “isResultBox” type = “boolean” default = “false” />
<aura:attribute name = “isFilterBox” type = “boolean” default = “false” />
<aura:attribute name = “selectedItem” type = “String” />
<aura:attribute name = “selectedFields” type = “List” default = “[]” />
<aura:attribute name = “resultList” type = “List” default = “[]” />
<aura:attribute name = “showSpinner” type = “Boolean” default = “false” />
<aura:attribute name = “datatypefield” type = “List” default = “[]” />
<!– Notification Library –>
<lightning:notificationsLibrary aura:id=”notifLib” />
<!– Lightning Button –>
<lightning:button variant = “brand” label = “Quick Report” title = “Click Here” onclick = “{! c.handleObjectItself }” />
<aura:if isTrue = “{!v.isOpenFieldBox}”>
<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”>
<lightning:buttonIcon iconName = “utility:close”
onclick = “{! c.closeModelBox }”
alternativeText = “close”
variant = “bare-inverse”
class = “slds-modal__close” />
<h2 id = “modal-heading-01” class = “slds-text-heading_medium slds-hyphenate”>Select a Field to be Shown as Header of Report</h2>
</header>
<div class = “slds-modal__content slds-p-around_medium” id=”modal-content-id-1″>
<table>
<th></th>
<tbody>
<aura:iteration items = “{!v.sobjectField}” var = “a”>
<tr>
<td><lightning:input type = “checkbox” name = “{!a}” value = “{!a}” onclick = “{!c.onClick}”></lightning:input></td>
<td>{!a}</td>
</tr>
</aura:iteration>
</tbody>
<aura:if isTrue = “{!v.showSpinner}”>
<lightning:spinner aura:id = “spinner” variant = “brand” alternativeText = “Rates” size = “medium” />
</aura:if>
</table>
</div>
<footer class = “slds-modal__footer”>
<lightning:button variant = “neutral”
label = “Previous”
title = “Previous”
onclick = “{! c.clickPrevious}”/>
<lightning:button variant = “neutral”
label = “Next”
title = “Next”
onclick = “{! c.clickNext}”/>
<lightning:button variant = “neutral”
label = “Cancel”
title = “Cancel”
onclick = “{! c.closeModelBox }”/>
</footer>
</div>
</section>
</aura:if>
<aura:if isTrue = “{!v.isFilterBox}”>
<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”>
<lightning:buttonIcon iconName = “utility:close”
onclick = “{! c.closeFilterBox }”
alternativeText = “close”
variant = “bare-inverse”
class = “slds-modal__close”/>
<h2 id = “modal-heading-01” class = “slds-text-heading_medium slds-hyphenate”>Please Apply Filter Criteria For Report</h2>
</header>
<div class = “slds-modal__content slds-p-around_medium” id = “modal-content-id-1”>
<div class = “row”>
<div class=”slds-grid slds-gutters”>
<div class=”slds-col”>
<lightning:select aura:id=”recordList” label=”{!v.sobjecttype}” >
<option value=””></option>
<aura:iteration items=”{! v.datatypefield}” var=”textField” >
<option value=”{! textField }”> {! textField } </option>
</aura:iteration>
</lightning:select>
</div>
<div class=”slds-col”>
<lightning:input name = “input1” aura:id = “selectIs” placeholder = “type the name of the records …” label = “Enter Some Text” />
</div>
</div>
<lightning:input type = “number” name = “input1” aura:id = “select” placeholder = “type the number of records…” label = “Enter a number” />
</div>
</div>
<footer class = “slds-modal__footer”>
<lightning:button variant = “neutral”
label = “Finish”
title = “Finish”
onclick = “{! c.finish}”/>
<lightning:button variant = “neutral”
label = “Previous”
title = “Previous”
onclick = “{! c.clickPreviousone}”/>
<lightning:button variant = “neutral”
label = “Cancel”
title = “Cancel”
onclick = “{! c.closeFilterBox}”/>
</footer>
</div>
<aura:if isTrue = “{!v.showSpinner}”>
<lightning:spinner aura:id = “spinner” variant = “brand” alternativeText = “Rates” size = “medium” />
</aura:if>
</section>
</aura:if>
<aura:if isTrue = “{!v.isResultBox}”>
<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”>
<lightning:buttonIcon iconName = “utility:close”
onclick = “{! c.closeResultBox }”
alternativeText = “close”
variant = “bare-inverse”
class = “slds-modal__close”/>
</header>
<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 slds-table_striped”>
<thead>
<tr></tr>
<tr class=”slds-text-title_caps”>
<aura:iteration items=”{!v.selectedFields}” var=”fieldName”>
<th scope=”col”>
<div class=”slds-truncate”>{!fieldName}</div>
</th>
</aura:iteration>
</tr>
</thead>
<tbody>
<aura:iteration items = “{!v.resultList}” var = “result”>
<tr>
<aura:iteration items = “{!v.selectedFields}” var = “fieldName”>
<td class = “slds-truncate”>
<c:childcmp object = “{!result}” fieldName = “{!fieldName}”/>
</td>
</aura:iteration>
</tr>
</aura:iteration>
</tbody>
</table>
</div>
<footer class = “slds-modal__footer”>
<lightning:button variant = “neutral”
label = “Cancel”
title = “Cancel”
onclick = “{! c.closeResultBox}”/>
</footer>
</div>
<aura:if isTrue = “{!v.showSpinner}”>
<lightning:spinner aura:id = “spinner” variant = “brand” alternativeText = “Rates” size = “medium” />
</aura:if>
</section>
</aura:if>
</aura:component>
Controller:
({
handleObjectItself : function(cmp, evnt, help) {
cmp.set(“v.showSpinner”,true);
help.generateObjectField(cmp, evnt, help);
},
closeModelBox : function(cmp, evnt, help) {
cmp.set(“v.isOpenFieldBox”, false);
},
handleGroupChange: function (cmp, evnt) {
var value = evnt.getParam(‘value’);
cmp.set(‘v.selection’, value);
},
handleBeforeSelect : function (cmp, evnt) {
cmp.set(‘v.selectedItem’, evnt.getParam(‘name’));
},
handleSelect : function (cmp, evnt,help) {
help.generateRelatedObjectField(cmp, evnt, help);
},
onClick : function (cmp, evnt) {
var tempHaulerChangedField = [];
var data = cmp.get(“v.selectedFields”);
if(data)
tempHaulerChangedField = data;
tempHaulerChangedField.push(evnt.getSource().get(“v.value”));
var uniqueArray = Array.from(new Set(tempHaulerChangedField));
cmp.set(“v.selectedFields”,uniqueArray);
},
clickNext : function (cmp, evnt,help) {
var action = cmp.get(“c.getStringField”);
cmp.set(“v.showSpinner”,true);
action.setParams({
“sObjectType” : cmp.get(“v.sobjecttype”),
});
action.setCallback(this,function(response){
var state = response.getState() ;
if(state == ‘SUCCESS’){
var res = response.getReturnValue();
if(!res.hasError){
if(!$A.util.isEmpty(res.sdata) && res.sdata.length > 0){
cmp.set(“v.datatypefield”,res.sdata);
cmp.set(“v.isOpenFieldBox”, false);
cmp.set(“v.isFilterBox”,true);
cmp.set(“v.showSpinner”,false);
}
}else{
var toastEvent = $A.get(“e.force:showToast”);
toastEvent.setParams({
“title”: “Error!”,
“message”: response.message,
“type” : “error”,
});
toastEvent.fire();
cmp.set(“v.showSpinner”,false);
}
}
});
$A.enqueueAction(action);
},
closeResultBox : function (cmp, evnt,help) {
cmp.set(“v.isResultBox”, false);
},
closeFilterBox : function (cmp, evnt,help){
cmp.set(“v.isFilterBox”, false);
},
clickPrevious : function (cmp,evnt,help){
cmp.set(“v.isOpenChooseBox”,true);
cmp.set(“v.isOpenFieldBox”,false);
},
clickPreviousone : function(cmp,evnt,help){
cmp.set(“v.selectedFields”,null);
cmp.set(“v.isOpenFieldBox”,true);
cmp.set(“v.isFilterBox”,false);
},
finish : function(cmp,evnt,help){
var sObjectType = cmp.get(“v.sobjecttype”);
var fieldsList = cmp.get(“v.selectedFields”);
var Limits = cmp.find(“select”).get(“v.value”);
var Types = cmp.find(“selectIs”).get(“v.value”);
var field = cmp.find(“recordList”).get(“v.value”);
if(Types != ” && field == ”)
cmp.find(‘notifLib’).showNotice({
“variant” : “Information”,
“header” : “Please Check the filter criteria!”,
“message” : “Please select the field to filter”,
});
else
help.goPage(cmp,evnt,sObjectType,fieldsList,Limits,Types,field);
},
AddNewRow : function(cmp,evnt,help){
cmp.getEvent(“AddRowEvt”).fire();
},
})
Helper:
({
generateObjectField : function(cmp, evnt, help) {
var action = cmp.get(“c.getRelatedSobjectName”);
cmp.set(“v.showSpinner”,true);
action.setParams({
“sObjectType” : cmp.get(“v.sobjecttype”),
});
action.setCallback(this,function(response){
var state = response.getState() ;
if(state == ‘SUCCESS’){
var res = response.getReturnValue();
if(!res.hasError){
if(!$A.util.isEmpty(res.sdata) && res.sdata.length > 0){
cmp.set(“v.sobjectField”,res.sdata);
cmp.set(“v.isOpenFieldBox”, true);
cmp.set(“v.showSpinner”,false);
}
}else{
var toastEvent = $A.get(“e.force:showToast”);
toastEvent.setParams({
“title” : “Error!”,
“message” : response.message,
“type” : “error”,
});
toastEvent.fire();
cmp.set(“v.showSpinner”,false);
}
}
});
$A.enqueueAction(action);
},
goPage : function(cmp,evnt,sObjectType,fieldsList,Limits,Types,field){
var action = cmp.get(“c.getFields”);
cmp.set(“v.showSpinner”,true);
action.setParams({
“sObjectType” : sObjectType,
“fieldsList” : fieldsList,
“Limits” : Limits,
“Types” : Types,
“field” : field,
});
action.setCallback(this,function(response){
var state = response.getState() ;
if(state == ‘SUCCESS’){
var res = response.getReturnValue();
if(!res.hasError){
if(!$A.util.isEmpty(res.sObjectData) && res.sObjectData.length > 0){
cmp.set(“v.resultList”,res.sObjectData);
cmp.set(“v.isOpenFieldBox”, false);
cmp.set(“v.isResultBox”, true);
cmp.set(“v.showSpinner”,false);
}else{
cmp.find(‘notifLib’).showNotice({
“variant” : “Information”,
“header” : “No Records found!”,
“message” : “Please Check the filter criteria.”,
});
cmp.set(“v.showSpinner”,false);
}
}else{
var toastEvent = $A.get(“e.force:showToast”);
toastEvent.setParams({
“title” : “Error!”,
“message” : response.message,
“type” : “error”,
});
toastEvent.fire();
cmp.set(“v.showSpinner”,false);
}
}
});
$A.enqueueAction(action);
},
})
Component : [Child Component]
<aura:component implements = “force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction”
access = “global” >
<aura:attribute name = “object” type = “Object” default = “” />
<aura:attribute name = “fieldName” type = “String” default = “” />
<aura:attribute name = “fieldValue” type = “String” access = “private” />
<aura:attribute name = “fields” type = “List” default = “[]” />
<aura:handler name = “init” value = “{!this}” action = “{!c.doInit}” />
<ui:outputText value=”{!v.fieldValue}”/>
</aura:component>
Controller:
({
doInit : function(component, event, helper) {
var rowData = component.get(‘v.object’);
var field = component.get(‘v.fieldName’);
component.set(“v.fields”,field);
component.set(‘v.fieldValue’,rowData[field]);
},
})
Client-side controller:
public class DynamicClass {
@AuraEnabled
public Boolean hasError;
@AuraEnabled
public String message;
@AuraEnabled
public List< String > sdata;
@AuraEnabled
public List< sObject > sObjectData;
@AuraEnabled
public static DynamicClass getRelatedSobjectName(String sObjectType){
DynamicClass controller = new DynamicClass();
controller.hasError = false;
try{
SObjectType objToken = Schema.getGlobalDescribe().get(sObjectType);
DescribeSObjectResult objDef = objToken.getDescribe();
Map< String, SObjectField > fields = objDef.fields.getMap();
List< String > fieldschema = new list< String >();
for(Schema.SObjectField sfield : fields.Values()) {
fieldschema.add(”+sfield);
}
if(fieldschema.Size() != null)
controller.sdata = fieldschema;
else
controller.sdata = null;
}catch(Exception e){
controller.hasError = true;
controller.message = ‘Exception:’+e.getMessage()+ ‘ ‘+e.getStackTraceString();
return controller;
}
return controller;
}
@AuraEnabled
public static DynamicClass getFields(String sObjectType, List< String > fieldsList, String Limits, String Types, String field){
DynamicClass controller = new DynamicClass();
controller.hasError = false;
try{
String Query;
//if the user gives the value in filter and leaves the Limits field empty
if(String.isEmpty(Limits) && !String.isEmpty(field) && String.isEmpty(Types)){
System.debug(‘if the user gives the value in filter and leaves the Limits field empty’);
Query = ‘SELECT ‘ +String.join(fieldsList, ‘,’);
Query += ‘ FROM ‘+sObjectType+ ‘ WHERE ‘+String.join(fieldsList, ‘!= null AND ‘)+’ != null AND ‘+field+ ‘ = null’;
}
//if the user gives the value in limits and leaves the filter field empty
else if(!String.isEmpty(Limits) && String.isEmpty(field) && String.isEmpty(Types)){
System.debug(‘if the user gives the value in limits and leaves the filter field empty’);
Query = ‘SELECT ‘ +String.join(fieldsList, ‘,’);
Query += ‘ FROM ‘+sObjectType+ ‘ WHERE ‘+String.join(fieldsList, ‘!= null AND ‘)+’ != null LIMIT ‘+Limits;
}
//if the user leaves both filter and Limits field as empty
else if(String.isEmpty(Limits) && String.isEmpty(field) && String.isEmpty(Types)){
System.debug(‘if the user leaves both filter and Limits as empty’);
Query = ‘SELECT ‘ +String.join(fieldsList, ‘,’);
Query += ‘ FROM ‘+sObjectType+ ‘ WHERE ‘+String.join(fieldsList, ‘!= null AND ‘)+’ != null’;
}
else if(String.isEmpty(Limits) && !String.isEmpty(field) && !String.isEmpty(Types)){
System.debug(‘if the user gives the value in filter and leaves the Limits field empty’);
Query = ‘SELECT ‘ +String.join(fieldsList, ‘,’);
Query += ‘ FROM ‘+sObjectType+ ‘ WHERE ‘+String.join(fieldsList, ‘!= null AND ‘)+’ != null AND ‘+field+ ‘ =: Types’;
}
//if the user gives the value in both filter and Limits
else{
System.debug(‘if the user gives the value in both filter and Limits’);
Query = ‘SELECT ‘ +String.join(fieldsList, ‘,’);
Query += ‘ FROM ‘+sObjectType+ ‘ WHERE ‘+String.join(fieldsList, ‘!= null AND ‘)+’ != null AND ‘+field+ ‘ =: Types LIMIT ‘+Limits;
}
System.debug(‘Query ===> ‘+Query);
List < sObject > returnList = new List < sObject > ();
List< sObject > ss = new List< sObject >();
ss = Database.query(Query);
for (sObject obj2: ss) {
returnList.add(obj2);
}
if(returnList.Size() != null)
controller.sObjectData = returnList;
else
controller.sdata = null;
}catch(Exception e){
controller.hasError = true;
controller.message = ‘Exception:’+e.getMessage()+ ‘ ‘+e.getStackTraceString();
return controller;
}
return controller;
}
@AuraEnabled
public static DynamicClass getStringField(String sObjectType){
DynamicClass controller = new DynamicClass();
controller.hasError = false;
try{
SObjectType objToken = Schema.getGlobalDescribe().get(sObjectType);
DescribeSObjectResult objDef = objToken.getDescribe();
Map< String, SObjectField > fields = objDef.fields.getMap();
List<String> fieldschema = new list<String>();
for(String sfield : fields.KeySet()) {
Schema.DescribeFieldResult describeResult = fields.get(sfield).getDescribe();
Schema.DisplayType s = objDef.fields.getMap().get(sfield).getDescribe().getType();
if(s == Schema.DisplayType.String){
fieldschema.add(”+sfield);
}
}
if(fieldschema.Size() != null)
controller.sdata = fieldschema;
else
controller.sdata = null;
}catch(Exception e){
controller.hasError = true;
controller.message = ‘Exception:’+e.getMessage()+ ‘ ‘+e.getStackTraceString();
return controller;
}
return controller;
}
}
Use Case:
Scenario:
- if the business wants to see all the records from an object or some of the records that meet a specific criterion, this component can be used.
Step 1 : Place the component anywhere in the record Detail page.
Step 2: By clicking the button, we can see all the fields from that object. Then select the fields and click the Next button.Step 3:
Next, set up the criteria and # of records to limit in the results.
For Ex:
I want to see the records whose billing country is USA.Step 4:
By clicking the Finish button, you can see a maximum of two records with billing country as USA.Step 5:
If no records are found, we show message in a dialog box.References: