I have implemented three dependent picklists like below:
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?