Thursday, October 8, 2015

Crazy Mistakes in Force.com Development II - Validation Error: Value is not valid - Salesforce apex:selectList

I have used <apex:selectList /> many times but recently I had a trouble with it; onchange event not firing only for some values.

<apex:actionRegion id="salesItemActionRegion">
         <apex:outputLabel value="Sales Item : "></apex:outputLabel>
         <apex:selectList id="drpSalesItem" value="{!selectedProduct}" size="1" >
              <apex:selectOptions id="optnSalesItem" value="{!ProductList}"></apex:selectOptions>
              <apex:actionSupport rerender="pnlProductInfo" action="{!LoadProductDetails}" event="onchange" />
         </apex:selectList>
 </apex:actionRegion>

Below are the selectOptions when I put them in a debug statement.

System.SelectOption[value="value abc", label="value abc", disabled="false"],
System.SelectOption[value="value  def", label="value  def", disabled="false"],
System.SelectOption[value="value ghi", label="value ghi", disabled="false"]

onchange action is firing for first and third values but nothing happens for second value. It's not hitting the action LoadProductDetails but surprisingly there are no any script errors in the console. Wasted a day and put an <apex:pageMessages /> component inside pnlProductInfo which gets rerender from my <apex:actionSupport/>.

Wooooh! here it's


But what's the validation that's failing here? It's some salesforce server side validation that checks for selectList options. If you noticed, in my second selectOption value, there is a DOUBLE SPACE between the words. Replaced it with a single space and working perfectly!

Cheers!


Monday, July 13, 2015

Pagination in apex with StandardSetController

When record count get increased, typical solution for most common problems like page size getting large, render time getting increased is applying pagination. Salesforce has a powerful mechanism to introduce pagination into your data set easily, with StandardSetController.

Here how you'll do it.

Your controller
public with sharing class ControllerPagination {
    Public Integer noOfRecords{get; set;}
    Public Integer size{get;set;}
    public ApexPages.StandardSetController standardSetCtrl {
        get{
            if(standardSetCtrl == null){
                size = 10;
                string queryString = 'Select Name, Type, BillingCity, BillingState, BillingCountry from Account order by Name';
standardSetCtrl = new ApexPages.StandardSetController(Database.getQueryLocator(queryString));
standardSetCtrl.setPageSize(size);
                noOfRecords = standardSetCtrl.getResultSize();
            }
            return standardSetCtrl;
        }set;
    }
     
    Public List<Account> getAccounts(){
        List<Account> accList = new List<Account>();
        for(Account a : (List<Account>)standardSetCtrl.getRecords())
            accList.add(a);
        return accList;
    }
     
    public pageReference refresh() {
        standardSetCtrl = null;
        getAccounts();
        standardSetCtrl.setPageNumber(1);
        return null;
    }
}
Your VF Page
<apex:page controller="ControllerPagination">
    <apex:form >
        <apex:pageBlock id="pb">
            <apex:pageBlockTable value="{!Accounts}" var="a">
                <apex:column value="{!a.Name}"/>
                <apex:column value="{!a.Type}"/>
                <apex:column value="{!a.BillingCity}"/>
                <apex:column value="{!a.BillingState}"/>
                <apex:column value="{!a.BillingCountry}"/>
            </apex:pageBlockTable>
            <apex:panelGrid columns="7">
                <apex:commandButton status="fetchStatus" reRender="pb" value="|<" action="{!standardSetCtrl.first}" disabled="{!!standardSetCtrl.hasPrevious}" title="First Page"/>
                <apex:commandButton status="fetchStatus" reRender="pb" value="<" action="{!standardSetCtrl.previous}" disabled="{!!standardSetCtrl.hasPrevious}" title="Previous Page"/>
                <apex:commandButton status="fetchStatus" reRender="pb" value=">" action="{!standardSetCtrl.next}" disabled="{!!standardSetCtrl.hasNext}" title="Next Page"/>
                <apex:commandButton status="fetchStatus" reRender="pb" value=">|" action="{!standardSetCtrl.last}" disabled="{!!standardSetCtrl.hasNext}" title="Last Page"/>
                <apex:outputText >{!(standardSetCtrl.pageNumber * size)+1-size}-{!IF((standardSetCtrl.pageNumber * size)>noOfRecords, noOfRecords,(standardSetCtrl.pageNumber * size))} of {!noOfRecords}</apex:outputText>
                <apex:commandButton status="fetchStatus" reRender="pb" value="Refresh" action="{!refresh}" title="Refresh Page"/>
                <apex:outputPanel style="color:#4AA02C;font-weight:bold">
                    <apex:actionStatus id="fetchStatus" startText="Fetching..." stopText=""/>
                </apex:outputPanel>
            </apex:panelGrid>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Thursday, June 25, 2015

Efficient SOQL For Loop

There are several governor limits to be considered when you are developing on Force.com platform. Among these, hitting the maximum number of queries allowed is the most common governor exception. Run time exception will be thrown as  System.Exception: Too many SOQL queries: 101.

This mainly occurs when you have put SOQL queries inside a loop as below.
for(Integer i=0; i<200; i++){
    Account act = [SELECT Id, Name FROM Account WHERE some_condition];
}


To avoid this you should somehow build the logic to meet the business matter and keep the query outside the loop.

Now the interesting part

Salesforce document says, "Developers should always use a SOQL for loop to process query results that return many records, to avoid the limit on heap size". What does this mean?

When you execute a standard SOQL query, it retrieve all the records while a for loop query does the same in chunks with SOAP API queryMore calls.


In addition to this, there are two formats of SOQL for loop in Force.com

  • Single sObject format where the for loop’s code block get executed once per sObject record(mostly used way).
  • sObject list format where the for loop’s code block get executed once per list of 200 sObjects

In this article what I need to highlight is the second format since it’s rarely seen in Force.com development. Have a look at the below snippet to get more clear idea
// keeping a savepoint so that transaction can be rolledback
Savepoint sp = Database.setSavepoint(); 

insert new Account[]{new Account(Name = 'AAA'), 
                     new Account(Name = 'AAA'), 
                     new Account(Name = 'AAA')};  //insert some data that can be identified

Integer i = 0;
Integer j;
for (Account[] tmp : [SELECT Id FROM Account WHERE Name = 'AAA']) {
    j = tmp.size();
    i++;
}
System.assert(j == 3); // The list should have contained the three accounts
                       // named 'AAA'
System.assert(i == 1); // Since a single batch can hold up to 200 records and,
                       // only three records should have been returned, the 
                       // loop should have executed only once

// Revert the database to the original state
Database.rollback(sp); 
Notes
  • You are safe to perform DMLs inside list format for loop queries than in a normal for loop
    query since the records are processed in chunks in list format (anyway this is not
    an encourage to perform DML inside a loop).
  • Since the queries having aggregate functions doesn't support queryMore function,
    you might get a runtime exception if you have such a query with more records in SOQL for loops.
  • When using the keyword ‘continue’ in list format for loops, it’ll skip to the next list
    of sObjects.