Skip to main content

Create dynamic object and fields drop-down in Salesforce.com

Allow user to create a filter rule for Out of office emails in Salesforce.com

There are many occurrence specially where we are enabling user to add dynamic rules , this requires us to replicate the product search screen where user can select an object where we can fetch the related field using dynamic apex and then add a pick-list where user can select the condition like EQUAL , INCLUDES. Which can be used to create rules which can be picked in Apex.

Use case : Allow user to create a filter rule for Out of office email filter



Here is the sample code to create this screen.

Page :-

<apex:page sidebar="false" controller="RuleManagerController">
  <apex:sectionHeader subtitle="Rule Manager"/>
  <style>
      .slds-button--brand.slds-button { margin-bottom:0.5rem; color: #fff; border: 1px solid #0070d2; background: #0070d2; font-size: 1em; padding: 2px 3px;}
  </style>
  <script type="text/javascript">
      
      function newPopup(url) {
           popupWindow = window.open(url,'popUpWindow','height=500,width=900,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,directories=no,status=yes')
      }  
      function clearAll(){
          var inputElems = document.getElementsByTagName("input");
          for(var i=0; i< inputElems.length; i++){
             if (inputElems[i].type === "text"){
                 inputElems.value = "";
             }
          }
      }

</script>
  <apex:outputPanel id="dummy"/>
  <apex:form id="theForm">
      <div class="bOverviewSearch" width="50%">
            <div class="view">
                <b style="color:blue">Choose the Object and respective field, then click "Create Rule" to create rule.</b> 
            </div>
      </div>
      <apex:pageBlock title="Select rule Criteria">
      <apex:outputpanel style="float:left;width:100%">
          <div class="searchFilterFieldsHolder searchFilterFieldsHolderFirst" id="searchArea">
                    
               <table class="searchFilterFields" width="100%">                     
                     <tbody id="theTBody1">  
                      <tr>
                        <td style="width:5%">
                          <span class="inputTitle" style="font-weight:bold">Select Object</span>
                          <br/>
                          <apex:SelectList value="{!strObjectName}" size="1" style="font-family: Arial;font-size: 11px;"  >
                                <apex:actionSupport event="onchange" status="status2" reRender="fieldlist"/>
                                <apex:selectOptions value="{!Objects}"></apex:selectOptions>
                          </apex:SelectList>
                        </td>  
                        <td style="width:5%">
                          <span class="inputTitle" style="font-weight:bold">Select Field</span>
                          <br/>
                          <apex:selectList value="{!selectedFilter}" size="1" style="font-family: Arial;font-size: 11px;" id="fieldlist">
                             <apex:selectOptions value="{!Fields}"/>
                             <apex:actionSupport event="onchange" action="{!populateOperatorOptions}" reRender="{!$Component.OperatorsMenu},{!$Component.filterStringText}" status="status2"/>
                          </apex:selectList>
                        </td>
                        <td style="width:5%">
                          <span class="inputTitle" style="font-weight:bold">Condition</span>
                          <br/>
                          <apex:selectList value="{!selectedOperator}" size="1" style="font-family: Arial;font-size: 11px;" id="OperatorsMenu">
                             <apex:selectOptions value="{!operatorOptions}"/>                             
                          </apex:selectList>
                        </td>
                        <td style="width:5%">
                          <span class="inputTitle" style="font-weight:bold">Value</span>
                          <br/>
                          <apex:outputPanel id="filterStringText">
                              <apex:inputText value="{!filterString}" style="font-family: Arial;font-size: 11px;" size="20" rendered="{!if(fieldPicklistvalues.size == 0,true,false)}"/>
                              <apex:selectList value="{!filterString}" size="1" style="font-family: Arial;font-size: 11px;" rendered="{!if(fieldPicklistvalues.size > 0,true,false)}">
                                 <apex:selectOptions value="{!fieldPicklistvalues}"/>
                              </apex:selectList>
                          </apex:outputPanel>
                        </td>
                        <td>
                          <span/>
                          <br/>                         
                          <apex:actionStatus id="status2">
                               <apex:facet name="start">                
                                  <div style="width: 1300px;">                   
                                     <img src="/img/loading24.gif" style="vertical-align:middle;"/>
                                     <span style="margin-left: 10px; font-size: 12px; font-weight: bold; color: #000000;">Please wait...</span> 
                                  </div>           
                               </apex:facet> 
                           </apex:actionStatus>
                        </td>
                      </tr>
                      
                       <tr id="ruleInfo">
                          <td colspan="4" style="width:100%">
                               <span class="inputTitle" style="font-weight:bold">Enter rule Body</span>
                               <br/>
                               <apex:inputTextarea cols="100" value="{!ruleCommnet}" style="font-family: Arial;font-size: 13px;" rows="5"/>
                      </td>
                       </tr>                  
                       
                    <tr id="filterInfo">
                      <td colspan="4" > 
                        <apex:outputPanel id="errorpanel" >
                           <br/> 
                           <apex:pageMessages /> 
                        </apex:outputPanel>
                         
                      </td>
                    </tr>
                     </tbody>
                  </table> 
                     
          </div>
      </apex:outputpanel>
      
      </apex:pageBlock>
      
      <apex:outputPanel id="outputpanelId">
      <apex:pageBlock title="All rule Rules" id="results" rendered="{!results.size > 0}">
          <apex:pageBlockTable value="{!results}" var="res">
              <apex:column headerValue="isActive">
                      <apex:outputField value="{!res.isActive__c}"/>
              </apex:column>             
              <apex:column headerValue="Object">
                      <apex:outputField value="{!res.Object__c}"/>
              </apex:column>
              <apex:column headerValue="Field">
                      <apex:outputField value="{!res.Field_Name__c}"/>
              </apex:column>
              <apex:column headerValue="Operator">
                      <apex:outputField value="{!res.Operator__c}"/>
              </apex:column>
              <apex:column headerValue="Value">
                      <apex:outputField value="{!res.Value__c}"/>
              </apex:column>                       
          </apex:pageBlockTable> 
      </apex:pageBlock>
      <apex:pageBlock id="noResultsBlock" rendered="{!results.size == 0}">
                <apex:outputPanel >No records to display.</apex:outputPanel> 
      </apex:pageBlock>
      </apex:outputPanel>
      
      
      
  </apex:form>
  
</apex:page>


Controller Class :-

public with sharing class RuleManagerController {    
    
    public Map<String, Schema.SoapType> additionalFiltersTypeMap;
    public Map<String, Schema.SOAPType> fieldTypeMap;
    public Map<Integer, FilterWrapper> extraFiltersMap {get;set;}
    public String selectedFilter {get;set;}
    public String selectedOperator {get;set;}
    public String notificationCommnet {get;set;}
    public String filterString {get;set;}
    public List<String> operatorsList {get;set;}
    public List<SelectOption> filterOptions {get;set;}
    public Boolean invalidFormat {get;set;}
    public String filterErrorMessage{get;set;} 
    public List<SelectOption> operatorOptions {get;set;}
    public Set<String> multiSelectMenuSet; 
    public list<SelectOption> fieldPicklistvalues{get;set;}
    public String strFieldName { get; set;}
    public String strObjectName{get; set;}
    private Set<String> ignoreSet;
    public List<Rule__c> results {get;set;}
    public Map<String,List<String>> mapPickListValues;
    public Map<String,String> fieldnameAPIMap = new Map<String,String>();
    public integer m_pageNumber = 0;
    public integer m_totalRecords = 0;
    private Integer CURRENT_PAGE_SIZE = 10;
    public List<Rule__c> paginatedResults {get;set;} 
    public List<Rule__c> displayRecord {get;set;}
    public Integer recordNumberDisplayed {get;set;}
    public String selectedID {get;set;}
    
    // Constructor
    public NotificationManagerController(){
    
        results = new List<Rule__c>();
        String productObjName = 'Account';
        extraFiltersMap = new Map<Integer, filterWrapper>();
        fieldTypeMap = new Map<String, Schema.SOAPType>();
        operatorsList = new List<String>{'equals', 'not equal to', 'starts with', 'contains', 'does not contain', 'less than', 'greater than', 'less or equal', 'greater or equal', 'includes', 'excludes'};
        filterOptions = new List<SelectOption>();
        invalidFormat = false;  
        multiSelectMenuSet = new Set<String>();
        filterOptions.add(new SelectOption('','--None--')); 
        additionalFiltersTypeMap = new Map<String,Schema.SoapType>{'Owner.Name' => SoapType.ID}; 
        ignoreSet = new Set<String>{'CreatedByIdId','LastViewedDate','LastReferencedDate','CurrencyIsoCode','RecordId','IsDeleted','LastModifiedById','Id','SystemModstamp','RecordTypeId'};
        fieldPicklistvalues = new List<SelectOption>();
        paginatedResults = new List<Rule__c>();
        String queryString='';
// Fetch the fields from rule table
        queryString = 'Select id , Body__c , Field_Name__c , Object__c , Operator__c , Value__c , isActive__c  from Rule__c';
        try{
            List<Rule__c> queryResult = (List<Rule__c>)Database.Query(queryString);
            results = new List<Rule__c>();
            
            for(Rule__c nrule : queryResult){
                results.add(nrule);
            }           
            system.debug('results ::' + results);            
        } catch(Exception ex){
            Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'An unexpected exception occured. Please contact your System Admin'));
            System.debug('###Exception : '+ex.getMessage());             
        }
    }    
   
    public List<SelectOption> getObjects(){

        List<SelectOption> options = new List<SelectOption>();
        options.add(new SelectOption('','--SELECT--'));
        Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe(); 
        Set<String> standardObjects = new Set<String>();
        for(Schema.SObjectType d : gd.values()){
            Schema.DescribeSObjectResult ds = d.getDescribe();
            if(!ds.isCreateable())
              continue;           
            standardObjects.add(ds.getName());           
        }
                    
        options.add(new SelectOption('Account','Account')); 
        options.add(new SelectOption('Case','Case')); 
        options.add(new SelectOption('Contact','Contact')); 
        options.add(new SelectOption('Lead','Lead')); 
        options.add(new SelectOption('Opportunity','Opportunity'));
         
        options.sort();        
        return options;
    }
    
    public List<selectOption> getFields(){ 
    
        List<SelectOption> options = new List<SelectOption>();
        List<SelectOption> filterOptions = new List<SelectOption>();
        filterOptions.add(new SelectOption('','--None--')); 
        system.debug('****strObjectName****' + strObjectName);
        // Get picklist Map
        mapPickListValues = new Map<String,List<String>>();
        if(strObjectName!=null && !''.equals(strObjectName)){
      
            Map<String, Schema.SObjectField> objectFields = Schema.getGlobalDescribe().get(strObjectName).getDescribe().fields.getMap();
            for(Schema.SObjectField s : objectFields.values())
            {
                 // Remove unwanted fields.
                 if(!ignoreSet.contains(s.getDescribe().getName())){
                     filterOptions.add(new SelectOption(s.getDescribe().getName(),s.getDescribe().getLabel()));
                     fieldTypeMap.put(s.getDescribe().getName(), s.getDescribe().getSoapType());
                     //Populate field API
                     fieldnameAPIMap.put(s.getDescribe().getLabel(),s.getDescribe().getName()); 
                     // Get picklist
                     Schema.DescribeFieldResult fieldResult = s.getDescribe();
                     if(fieldResult.getPicklistValues()!=null){
                         List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
                         List<String> lstPickvals = new List<String>();
                         for( Schema.PicklistEntry f : ple){
                              lstPickvals.add(f.getValue());                         
                         }
                         mapPickListValues.put(s.getDescribe().getName(),lstPickvals);
                     }
                 }
            }
            
            
        }
        populateOperatorOptions();
        
        filterOptions.sort();
        system.debug('****fieldnameAPIMap****' + fieldnameAPIMap);
        return filterOptions;
    }
    
    public void getCondition(String field, String operator, String value){
    try{
    System.debug('@@@@@@'+fieldTypeMap);
        String returnString = '';
        Schema.SoapType fieldType;
        if(field == '' || field == NULL || operator == '' || operator == NULL || value == '' || value == NULL){
            return ;
        }
        Boolean isValid = validateCondition(field, value);
        System.debug('isValid::'+isValid);
        if(!isValid){
            invalidFormat = true;
            Schema.SoapType fType;
            if(fieldTypeMap.containsKey(field)){
               fType = fieldTypeMap.get(field);
            }
                           
            if(fType == SoapType.Boolean){               
                filterErrorMessage = field +' has to be either True or False.';
                Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,filterErrorMessage));
               
                
            }else if(fType == SoapType.Integer || fType == SoapType.Double){              
                filterErrorMessage = field +' has to be in the range 0-9.';
                Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,filterErrorMessage));
                
            }else if(fType == SoapType.DateTime){              
                filterErrorMessage = field +' has to be in the format yyyy-mm-dd hh:mm:ss.';
                Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,filterErrorMessage));
                
            }else if(fType == SoapType.Date){              
                filterErrorMessage = field +' has to be in the format yyyy-mm-dd.';
                Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,filterErrorMessage));
               
            }          
            return ;
        } else{
            invalidFormat = false;
        }

     
        }
        catch(Exception e){
          system.debug('...........'+e);
          return ;
        }
    }
    
    public Boolean validateCondition(String field, String value){       
        Boolean returnBoolean = false;
        Schema.SoapType fieldType;
        if(fieldTypeMap.containsKey(field)){
            fieldType = fieldTypeMap.get(field);
        }else if(additionalFiltersTypeMap.containsKey(field)){
            fieldType = additionalFiltersTypeMap.get(field);
        }
        system.debug('****' + fieldType);
        if(fieldType == SoapType.Integer || fieldType == SoapType.Double){
            Pattern numberFormat = Pattern.Compile('^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$'); 
            Matcher numberMatch = numberFormat.matcher(value);
            if(numberMatch.Matches()){
                returnBoolean = true;
            }
        } else if(fieldType == SoapType.Boolean){
            if(value.equalsIgnoreCase('TRUE') || value.equalsIgnoreCase('FALSE') || value == '0' || value == '1'){
                returnBoolean = true;
            }
        } else if(fieldType == SoapType.Date){
            if(value.equalsIgnoreCase('TODAY')){
                returnBoolean = true;
            } else{
                Pattern dateFormat = Pattern.Compile('(^\\(?(\\d{4})\\)?[- ]?(\\d{2})[- ]?(\\d{2})$)');
                Matcher dateMatch = dateFormat.Matcher(value);
                if(dateMatch.Matches()){
                    returnBoolean = true;
                }
            }
        } else if(fieldType == SoapType.DateTime){
            if(value.equalsIgnoreCase('NOW')){
                returnBoolean = true;
            } else{
                Pattern dateTimeFormat = Pattern.Compile('(^\\(?(\\d{4})\\)?[- ]?(\\d{2})[- ]?(\\d{2})[ ]?(\\d{2})[: ]?(\\d{2})[: ]?(\\d{2})$)');
                Matcher dateTimeMatch = dateTimeFormat.Matcher(value);
                if(dateTimeMatch.Matches()){
                    returnBoolean = true;
                }
            }
        } else if(fieldType == SoapType.String){
            returnBoolean = true;
        }
        else if(fieldType == SoapType.Id){
            returnBoolean = true;
        }
        System.debug('returnBoolean ::'+returnBoolean );
        return returnBoolean;
    }
    
    public Class FilterWrapper{
        public List<SelectOption> fOptions {get; set;}
        public List<SelectOption> oOptions {get; set;}
        public List<SelectOption> pOptions {get; set;}
        public String fSelected {get; set;}
        public String oSelected {get; set;}
        public String value {get; set;}
        public List<String> allOperators;
        public Map<String, Schema.SoapType> fieldTypeMap;
        public Set<String> multiSelectMenuSet;
        public Map<String, Schema.SoapType> additionalFiltersTypeMap;       
        public boolean isPickList{get; set;}
        
        public FilterWrapper(List<SelectOption> fOptions, List<String> operators, Map<String, Schema.SoapType> ftMap, Set<String> mmSet, Map<String, Schema.SoapType> afMap){
            this.fOptions = new List<SelectOption>();
            this.fOptions = fOptions;
            this.allOperators = new List<String>();
            this.allOperators = operators;
            fieldTypeMap = new Map<String, Schema.SoapType>();
            fieldTypeMap = ftMap;
            additionalFiltersTypeMap = new Map<String, Schema.SoapType>();
            additionalFiltersTypeMap = afMap;
            multiSelectMenuSet = new Set<String>();
            multiSelectMenuSet = mmSet;
            oOptions = new List<SelectOption>();
            oOptions.add(new SelectOption('', '--None--'));
            for(String str : allOperators){
                oOptions.add(new SelectOption(str, str));    
            }
            isPickList = false;           
        }
        
        public void populateOperatorOptions(){
            oOptions = new List<SelectOption>();
            oOptions.add(new SelectOption('','--None--'));
            List<String> filteredOperators = new List<String>();
            if(fSelected == '' || fSelected == NULL){
                filteredOperators = allOperators;            
            } else{
                Schema.SoapType fieldType;
                if(fieldTypeMap.containsKey(fSelected)){
                    fieldType = fieldTypeMap.get(fSelected);
                } else if(additionalFiltersTypeMap.containsKey(fSelected)){
                    fieldType = additionalFiltersTypeMap.get(fSelected);
                }
                if(fieldType == SoapType.Boolean){
                    filteredOperators = new List<String>{'equals', 'not equal to'};
                } else if(fieldType == SoapType.Integer || fieldType == SoapType.Double || fieldType == SoapType.Date || fieldType == SoapType.DateTime || fieldType == SoapType.Time){
                    filteredOperators = new List<String>{'equals', 'not equal to', 'less than', 'greater than', 'less or equal', 'greater or equal'};
                } else if(fieldType == SoapType.String || fieldType == SoapType.ID){
                    filteredOperators = new List<String>{'equals', 'not equal to', 'starts with', 'contains', 'does not contain', 'less than', 'greater than', 'less or equal', 'greater or equal'};
                    if(multiSelectMenuSet.contains(fSelected)){
                        filteredOperators.add('includes');
                        filteredOperators.add('excludes');
                    }
                }
            } 
            
            for(String str : filteredOperators){
                oOptions.add(new SelectOption(str, str));
            }
            isPickList = false;
            pOptions = new List<SelectOption>();
            pOptions.add(new SelectOption('', '--None--'));
            
        }
    }
    
    public PageReference populateOperatorOptions(){
        operatorOptions = new List<SelectOption>();
        //filterString = '';
        operatorOptions.add(new SelectOption('','--None--'));
        List<String> filteredOperators = new List<String>();
        if(selectedFilter == '' || selectedFilter == NULL){
            filteredOperators = operatorsList;            
        } else{
            Schema.SoapType fieldType;
            if(fieldTypeMap.containsKey(selectedFilter)){
                fieldType = fieldTypeMap.get(selectedFilter);
            }else if(additionalFiltersTypeMap.containsKey(selectedFilter)){
                fieldType = additionalFiltersTypeMap.get(selectedFilter);
            }
            if(fieldType == SoapType.Boolean){
                filteredOperators = new List<String>{'equals', 'not equal to'};
            } else if(fieldType == SoapType.Integer || fieldType == SoapType.Double || fieldType == SoapType.Date || fieldType == SoapType.DateTime || fieldType == SoapType.Time){
                filteredOperators = new List<String>{'equals', 'not equal to', 'less than', 'greater than', 'less or equal', 'greater or equal'};
            } else if(fieldType == SoapType.String || fieldType == SoapType.ID){
                filteredOperators = new List<String>{'equals', 'not equal to', 'starts with', 'contains', 'does not contain', 'less than', 'greater than', 'less or equal', 'greater or equal'};
                if(multiSelectMenuSet.contains(selectedFilter)){
                    filteredOperators.add('includes');
                    filteredOperators.add('excludes');
                }
            }
        }
        for(String str : filteredOperators){
            operatorOptions.add(new SelectOption(str, str));
        }
        System.debug('SelectedFilter******'+selectedFilter);       
        if(mapPickListValues!=null && !mapPickListValues.isEmpty()){        
            System.debug('MapValues&&&&'+mapPickListValues);
            fieldPicklistvalues = new List<SelectOption>();
            if(mapPickListValues.containsKey(selectedFilter)){
                for(String s : mapPickListValues.get(selectedFilter)){
                    fieldPicklistvalues.add(new SelectOption (s,s));
                }
                System.debug('MapValues&&&&'+mapPickListValues+'&&&&&'+fieldPicklistvalues);
            }
        }
        return NULL;
    }
    
    
}

Comments

Popular posts from this blog

Einstein Bot user authentication

Using Bot for data manipulation use case in your company requires need of implementing some extra security layer to make it spoof proof. One of exciting salesforce feature we have is the well known Einstein Bot which many companies have implemented. I am going to cover detail step-by-step implementation of User validation use case using encrypted token. STEP-1: Create a Site & Site User go to setup > Sites & Domains > Sites Create a Site and make your user as "Site Contact". This is a prerequisites for live agent or Embedded Service setup. STEP-2 : Create a Embedded Service(formerly snap-ins) go to Service setup > Embedded Service Create a new Embedded Service Deployment record and copy the embedded service code snipped generated in a notepad. STEP-3  : Create a Visualforce page to test the chatbot (it will simulate the actual web portal which is going to consume the embedded service snipped.) BotController.apxc public class BotControlle...

Dynamically populate lightning:combobox

In order to dynamically populate values in lightning:combobox using javascript we can create a list variable and push the values. We also require the values like ids associated to the particular option for back-end logic. For example  : If we want to show the list of contacts using lightning:combobox component where the option should display the contact name but the value should be the contact id which will be used for back-end logic For this we can create a temporary Map variable in our lightning component and set the value and label parameters of the lightning:combobox. Here is the code for this component Combo.app <aura:application extends="force:slds" access="global" description="Lightning Combo Demo"> <c:Combo_Cmpt/> </aura:application> Combo_Cmpt.cmp <aura:component implements="flexipage:availableForAllPageTypes" access="global" controller="ComboController"> <!-- ...

Use of wrapper class in lightning:datatable

As you guys know the wrapper class concept in visualforce pages , we generally use it to create a data-table which fetches data from different objects or if we want to redirect user to some other page on click of a link as one of the column of data-table.        For example we want a column "Account Name" on the data-table which is a link and once user clicks it should redirect respective account record. Or , suppose we want to display a column with some images or icons as a visual indicator Or what not. These requirements require us to use a wrapper on the lighting data-table (lightning:datatable) I am going to use my previous account search example ( Account Search Lightning Component ) and explain the use of wrapper. AccountSearchWrapper.app <aura:application extends="force:slds" access="global" >     <c:AccountSearchWrapper /> </aura:application> AccountSearchWrapper.cmp <aura:component controller="...