[SalesForce] action doesn’t work every time

I have implemented three dependent picklists like below:

enter image description here

There is one visualforce page (standard controller for Opportunity with extension). This problem is connected with an action for Search button. Sometimes function searchButtonExecute() is not executed.

Below I enclosure a piece of vf code, tags attributes can be important:

<apex:form >        
        <apex:pageBlock title="Find Products" rendered="true" id="filterPicklists">  
            <apex:pageBlockSection >
                <apex:pageBlockSectionItem >
                    <apex:actionRegion >
                        <apex:panelGrid columns="2">
                            <apex:outputLabel value="Product Family" style="display:block; width: 130px" />
                            <apex:selectList size="1" label="Product Family" value="{!selectedProductFamilyValue}" id="prodFamily" style="width: 500px">
                                <apex:selectOptions value="{!selectProductFamilies}" />                             
                                <apex:actionSupport action="{!setCategoriesList}" event="onchange" reRender="categories, subcategories" />      
                            </apex:selectList>
                        </apex:panelGrid>
                    </apex:actionRegion>
                </apex:pageBlockSectionItem>
                <br/>
                <apex:pageBlockSectionItem >
                    <apex:actionRegion >
                        <apex:panelGrid columns="2">
                            <apex:outputLabel value="Category" style="display:block; width: 130px" />
                            <apex:selectList size="1" label="Category" value="{!selectedCategoriesValue}" id="categories" style="width: 500px">
                                <apex:selectOptions value="{!selectCategories}" />  
                                <apex:actionSupport action="{!setSubCategoriesList}" event="onchange" reRender="subcategories" />   
                            </apex:selectList>      
                        </apex:panelGrid>                       
                    </apex:actionRegion>
                </apex:pageBlockSectionItem>    
                <br/>
                <apex:pageBlockSectionItem >
                    <apex:actionRegion >
                        <apex:panelGrid columns="2">
                            <apex:outputLabel value="Sub Category" style="display:block; width: 130px" />
                            <apex:selectList size="1" label="SubCategory" value="{!selectedSubCategoriesValue}" id="subcategories" style="width: 500px"> 
                                <apex:selectOptions value="{!selectSubCategories}" />   
                            </apex:selectList>  
                        </apex:panelGrid>
                    </apex:actionRegion>
                </apex:pageBlockSectionItem>                        
            </apex:pageBlockSection>                
            <apex:pageBlockButtons location="bottom">
                <apex:commandButton value="Search" action="{!searchButtonExecute}" 
                    reRender="selectedProductsBlock" onclick="blockPage();" onComplete="j$.unblockUI();"  />
                <apex:commandButton value="Clear Filter" action="{!clearButtonExecute}" reRender="filterPicklists" 
                    onclick="blockPage();" onComplete="j$.unblockUI();" />
            </apex:pageBlockButtons>                                            
        </apex:pageBlock>       
    </apex:form>  

When user takes value for first picklists, values for second one are loading dynamic. For third the same way (after takes value for second picklist). Then after click Search button a section "selectedProductsBlock" should be rendered (after executing query).

A piece of my controller class is:

public class AddNewProductController {

    public Opportunity opp;
    public String pricebookName {get; private set; }
    public Pricebook2 pricebook {get; private set; }    
    public List<PricebookEntry> pricebookEntry {get; private set; }

    public String selectedProductFamilyValue {get; set; }
    public List<SelectOption> selectProductFamilies {get ; set; }
    public String selectedCategoriesValue {get; set; }
    public List<SelectOption> selectCategories {get; set; }
    public String selectedSubCategoriesValue {get; set; }
    public List<SelectOption> selectSubCategories {get; set; }
    public List<SelectableProduct> selectableProducts {get; private set; }
    public List<CheckableProduct> checkedProducts {get; set; }          

    public AddNewProductController(ApexPages.StandardController standardController) {       
                    this.opp = (Opportunity) standardController.getRecord();

            if(this.opp.Pricebook2Id == null) {
                ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'First choose Pricebook for Products! Cancel selecting Products.'));
                return;
            }           

            this.pricebook = [SELECT Id, Name FROM Pricebook2 WHERE Id = :this.opp.Pricebook2Id];   
            this.pricebookName = pricebook.Name;        

            setProductFamiliesList();

            this.selectableProducts = new List<SelectableProduct>();    
            this.checkedProducts = new List<CheckableProduct>();

            this.selectCategories = new List<SelectOption>();
            this.selectSubCategories = new List<SelectOption>();
        }   

    public void setProductFamiliesList() {

        List<SelectOption> options = new List<SelectOption>();          

        this.pricebookEntry = new List<PricebookEntry>();
        this.pricebookEntry = [SELECT Product2.Family, Product2.Id, Id
                                    FROM PricebookEntry
                                    WHERE Pricebook2Id = :this.opp.Pricebook2Id];

        Set<String> families = new Set<String>();                                   
        for(PricebookEntry p : this.pricebookEntry) {   
            families.add(p.Product2.Family);
        }

        List<String> listFamilies = new List<String>(families);
        listFamilies.sort();

        options.add(new Selectoption('', ''));  //default value

        for(String f : listFamilies) {  
            if(f != null) {
                options.add(new Selectoption(f, f));                            
            }
        }

        this.selectProductFamilies = options;       
    }

    public void setCategoriesList() {                               
        List<SelectOption> options = new List<SelectOption>();      
        List<AggregateResult> categories = [SELECT Ad_Category__c 
                                    FROM Product2 
                                    WHERE Family = :selectedProductFamilyValue
                                    GROUP BY Ad_Category__c 
                                    ORDER BY Ad_Category__c];

        Set<String> setCats = new Set<String>();
        for(AggregateResult result : categories) {
            setCats.add((String) result.get('Ad_Category__c'));
        }                               

        options.add(new Selectoption('', ''));  //default value

        List<String> listCats = new List<String>(setCats);
        listCats.sort();

        for(String s : listCats) {  
            if(s != null) {
                options.add(new Selectoption(s, s));    
            }                           
        }       

        this.selectCategories = options; 
    }

    public void setSubCategoriesList() {
        List<SelectOption> options = new List<SelectOption>();  

        String subCategoryInnerClause = (this.selectedSubCategoriesValue != null && !String.isEmpty(this.selectedSubCategoriesValue)) ?
                                        ' AND Ad_Sub_Category__c = ' + '\'' + selectedSubCategoriesValue + '\'' : '';

        String query = 'SELECT Ad_Sub_Category__c '
                        + ' FROM Product2'
                        + ' WHERE Ad_Category__c = '  + '\'' + this.selectedCategoriesValue  + '\'' +
                        + subCategoryInnerClause +
                        + ' GROUP BY Ad_Sub_Category__c'
                        + ' ORDER BY Ad_Sub_Category__c';

        List<AggregateResult> subcategories = new List<AggregateResult>();

        try {           
            subcategories = (List<AggregateResult>) Database.query(query);  
        }
        catch(System.QueryException e) {
            System.debug(e.getTypeName());      
            return;
        }   

        Set<String> setSubCats = new Set<String>();
        for(AggregateResult result : subcategories) {
            setSubCats.add((String) result.get('Ad_Sub_Category__c'));
        }   

        options.add(new Selectoption('', ''));  //default value

        List<String> listSubCats = new List<String>(setSubCats);
        listSubCats.sort();                     

        for(String s : listSubCats) {   
            if(s != null) {
                options.add(new Selectoption(s, s));    
            }                           
        }       

        this.selectSubCategories = options; 
    }

    public void **searchButtonExecute**() {         
        List<Product2> products = new List<Product2>();

        String productFamiliyClause = (this.selectedProductFamilyValue != null && !String.isEmpty(this.selectedProductFamilyValue)) ?
                                ' WHERE Family = ' + '\'' + this.selectedProductFamilyValue + '\'' : '';
        String categoryClause = (!String.isEmpty(productFamiliyClause) && this.selectedCategoriesValue != null && !String.isEmpty(this.selectedCategoriesValue)) ?
                                ' AND Ad_Category__c = ' + '\'' + this.selectedCategoriesValue + '\'' : '';
        String subcategoryClause = (!String.isEmpty(productFamiliyClause) && !String.isEmpty(categoryClause) &&
                                            this.selectedSubCategoriesValue != null && !String.isEmpty(this.selectedSubCategoriesValue)) ?
                        ' AND Ad_Sub_Category__c = ' + '\'' + this.selectedSubCategoriesValue + '\'' : '';

        String query = 'SELECT Id, Name, ProductCode, Description, Family, Exhibition_Name__c, Ad_Category__c, Ad_Sub_Category__c, ' +
                                                ' (SELECT Id, UnitPrice, Pricebook2Id FROM PricebookEntries) ' +
                                    ' FROM Product2' +
                                    + productFamiliyClause + categoryClause + subcategoryClause +
                                    ' ORDER BY Name, ProductCode, Ad_Category__c, Ad_Sub_Category__c ' +                                    
                                    ' LIMIT 500 ';


        try {           
            products = (List<Product2>) Database.query(query);  
        }
        catch(System.QueryException e) {
            System.debug(e.getTypeName());      
            return;
        }                               

        for(Product2 p : products) {
            Double unitPrice = 0;

            for(PricebookEntry pe : p.PricebookEntries) {
                if(pe.Pricebook2Id == this.pricebook.Id) {
                    unitPrice = pe.UnitPrice;                   
                    break;
                }
            }

            this.selectableProducts.add(new SelectableProduct(p, unitPrice));
        }
    }


    public class SelectableProduct {
        public Boolean selected { get; set; }
        public Product2 product { get; private set; }
        public Decimal listPrice { get; private set; }

        public SelectableProduct(Product2 product, Decimal unitPrice) {
            this.product = product;
            this.selected = false;
            this.listPrice = unitPrice;
        }
    }


}

Why method searchButtonExecute() is not executed every time I clicked the button? What is interesting onclick, oncomplete javascript functions are executed every time. This problem appears in further iterations (not during first processing after first use button).

Any idea what needs to be changed?

Best Answer

I think the problem you are having is related to reRender happening before the action completes.

Try split out the steps to make sure they are executed in the right order.

<apex:commandButton value="Search" reRender="none" onclick="blockPage();" oncomplete="actExecuteSearch()"  />

 <apex:actionFunction action="{!searchButtonExecute}" name="actExecuteSearch" oncomplete="actFinally()" reRender="none"/>

 <apex:actionFunction name="actFinally" reRender="selectedProductsBlock" onComplete="j$.unblockUI();" />

See if this works.

Related Topic