1

I have problem with dependent selectlists in apex:pageblocktable. Here's my code:

public class MyController {

    public MyController() {}

    public Id selectedCarId{get; set;}
    public Id selectedBrandId{get; set;}
    public Id selectedTypeId{get; set;}
    public Id selectedYeardId{get; set;}

    public List<CarWrapper> items; //List for items
    public List<CarWrapper> items2;

    public List<CarWrapper> getItems(){
        items = new List<CarWrapper>(items2);
        CarWrapper cw = new CarWrapper(selectedCarId);
        items.add(cw);
        return items;
    }

    public PageReference addNew(){
        if(items != null){
            items2 = new List<CarWrapper>(items); //copy of table before 'Add new row'
            items.add(new CarWrapper(null));
            System.debug('add row');
        }
        return null;
    }

    public class CarWrapper{
        public List<SelectOption> cars {get; set;}
        public List<SelectOption> brands{get; set;}
        public List<SelectOption> types{get; set;}
        public List<SelectOption> years{get; set;}

        public CarWrapper(Id selectedCarId){
            this.cars = getCarsSelectOptions();
            this.brands = getBrandSelectOptions(selectedCarId);
            this.types = getTypesSelectOptions(selectedCarId);
            this.years = getYearsSelectOptions(selectedCarId);
        }

        public List<SelectOption> getCarsSelectOptions(){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Car__c car: [SELECT Id, Name FROM Car__c]){
                s.add(new selectoption(car.Id, car.Name));
            }
            return s;
        }

        Map<Id, List<Brand__c>> carBrandMap = new Map<Id, List<Brand__c>>();

        public List<SelectOption> getBrandSelectOptions(Id carId){
            List<Brand__c> lstBrand = new List<Brand__c>();

            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            if(carBrandMap.containsKey(carId))
            {
                lstBrand = carBrandMap.get(carId);
            }
            else
            {
                lstBrand = [SELECT Id, Name FROM Brand__c WHERE Car__c=: carId];
                carBrandMap.put(carId,lstBrand);
            }

            for(Brand__c brand: lstBrand){
                s.add(new selectoption(brand.Id, brand.Name));
            }
            return s;
        }

        public List<SelectOption> getTypesSelectOptions(Id carId){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Type__c type: [SELECT Id, Name FROM Type__c WHERE Car__c=: carId]){
                s.add(new selectoption(type.Id, type.Name));
            }
            return s;
        }

        public List<SelectOption> getYearsSelectOptions(Id carId){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Year__c year: [SELECT Id, Name FROM Year__c WHERE Car__c=: carId]){
                s.add(new selectoption(year.Id, year.Name));
            }
            return s;
        }
    }
}

Visualforce Page

<apex:page controller="MyController">
    <apex:form >

    <apex:pageBlock title="My Content">

        <apex:pageBlockTable value="{!items}" var="item" id="itemsTable">
            <apex:column headerValue="Car">
                <apex:selectList value="{!selectedCarId}" size="1" id="carSelect">
                    <apex:selectOptions value="{!item.cars}"/>
                    <apex:actionSupport event="onchange" reRender="brandSelect"/>
                </apex:selectList>
            </apex:column>

            <apex:column headerValue="Brand">
                <apex:selectList value="{!selectedBrandId}" size="1" id="brandSelect">
                    <apex:selectOptions value="{!item.brands}"/>
                    <apex:actionSupport event="onchange" reRender="typeSelect"/>
                </apex:selectList>
            </apex:column>

            <apex:column headerValue="Type">
                <apex:selectList value="{!selectedTypeId}" size="1" id="typeSelect">
                    <apex:selectOptions value="{!item.types}"/>
                    <apex:actionSupport event="onchange" reRender="yearSelect"/>
                </apex:selectList>
            </apex:column>

            <apex:column headerValue="Year">
                <apex:selectList value="{!selectedYeardId}" size="1" id="yearSelect">
                    <apex:selectOptions value="{!item.years}"/>
                </apex:selectList>
            </apex:column>

        </apex:pageBlockTable>
        <apex:commandButton value="Add new" action="{!addNew}" reRender="itemsTable"/>

    </apex:pageBlock>
    </apex:form>
</apex:page>

Use cases:

  1. One row - everything work's as I expected. enter image description here
  2. Add row by click the button "Add new" enter image description here

Other issues: - How can I delete "selected" values from first picklist?

2
+50

You are correct, the problem is with duplicated selectlist's id's. You have to keep the selected value unique for each selectlist, or else they will all overwrite into the same variable and you won't be able to differentiate between each row.

If you have a list of sObjects, you'd have a wrapper class which holds the sObject and the selected value. In this way, you can keep separately selected value for each row.

So instead of keeping the selectedCarId, selectedBrandId, selectedTypeId, selectedYeardId variables at Controller level, you have to move them inside the wrapper class. By doing this you can maintain differently selected values for each wrapper object.

I tried to modify your code as below. Hope this will help.

VF Page

<apex:page controller="MyController5" >
    <apex:form>
        <apex:pageBlock title="My Content">
            <apex:variable var="rowNumber" value="{!0}"/>
            <apex:pageBlockTable value="{!items}" var="item" id="itemsTable">
                <apex:column headerValue="Car">
                    <apex:selectList value="{!item.selectedCarId}" size="1" >
                        <apex:selectOptions value="{!item.cars}"/>
                        <apex:actionSupport action="{!updateRow}" event="onchange" reRender="brandSelect"/>
                    </apex:selectList>
                </apex:column>
                <apex:column headerValue="Brand">
                    <apex:selectList value="{!item.selectedBrandId}" size="1" id="brandSelect">
                        <apex:selectOptions value="{!item.brands}"/>
                        <apex:actionSupport event="onchange" reRender="typeSelect"/>
                    </apex:selectList>
                </apex:column>

                <apex:column headerValue="Type">
                    <apex:selectList value="{!item.selectedTypeId}" size="1" id="typeSelect">
                        <apex:selectOptions value="{!item.types}"/>
                        <apex:actionSupport event="onchange" reRender="yearSelect"/>
                    </apex:selectList>
                </apex:column>

                <apex:column headerValue="Year">
                    <apex:selectList value="{!item.selectedYeardId}" size="1" id="yearSelect">
                        <apex:selectOptions value="{!item.years}"/>
                    </apex:selectList>
                </apex:column>                
            </apex:pageBlockTable>
            <apex:commandButton value="Add new" action="{!addNew}" reRender="itemsTable"/>
            <apex:commandButton value="Show Selection" reRender="showsel"/>
            <apex:pageBlockTable value="{!items}" var="item" id="showsel">
                <apex:column value="{!item.selectedCarId}" headerValue="Car"/>
                <apex:column value="{!item.selectedBrandId}" headerValue="Brand"/>
                <apex:column value="{!item.selectedTypeId}" headerValue="Type"/>
                <apex:column value="{!item.selectedYeardId}" headerValue="Year"/>               
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Controller

public class MyController5 {

    public List<CarWrapper> items {get;set;}    

    public MyController5() {
        items = new List<CarWrapper>();
        CarWrapper cw = new CarWrapper();
        items.add(cw);
    }
    public PageReference addNew(){
        if(items != null){
            CarWrapper cw = new CarWrapper();
            items.add(cw);
        }
        return null;
    }    
    public PageReference updateRow(){
        String selectedCarId;
        for(CarWrapper carwrp:items){
            selectedCarId = carwrp.selectedCarId;
            carwrp.brands = carwrp.getBrandSelectOptions(selectedCarId);
            carwrp.types = carwrp.getTypesSelectOptions(selectedCarId);
            carwrp.years = carwrp.getYearsSelectOptions(selectedCarId);
        }
        return null;
    }

    public class CarWrapper{

        public String selectedCarId{get; set;}
        public String selectedBrandId{get; set;}
        public String selectedTypeId{get; set;}
        public String selectedYeardId{get; set;}

        public List<SelectOption> cars {get; set;}
        public List<SelectOption> brands{get; set;}
        public List<SelectOption> types{get; set;}
        public List<SelectOption> years{get; set;}        

        Map<Id, List<Brand__c>> carBrandMap = new Map<Id, List<Brand__c>>();

        public CarWrapper(){
            this.cars = getCarsSelectOptions();
            this.brands = new list<selectoption> { new selectoption('','Select one') };
            this.types = new list<selectoption> { new selectoption('','Select one') };
            this.years = new list<selectoption> { new selectoption('','Select one') };            
        }
        public CarWrapper(String selectedCarId){
            this.cars = getCarsSelectOptions();
            this.brands = getBrandSelectOptions(selectedCarId);
            this.types = getTypesSelectOptions(selectedCarId);
            this.years = getYearsSelectOptions(selectedCarId);
        }
        public List<SelectOption> getCarsSelectOptions(){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Car__c car: [SELECT Id, Name FROM Car__c]){
                s.add(new selectoption(car.Id, car.Name));
            }
            return s;
        }        
        public List<SelectOption> getBrandSelectOptions(String carId){
            List<Brand__c> lstBrand = new List<Brand__c>();

            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            if(carBrandMap.containsKey(carId)){
                lstBrand = carBrandMap.get(carId);
            }else{
                lstBrand = [SELECT Id, Name FROM Brand__c WHERE Car__c=: carId];
                carBrandMap.put(carId,lstBrand);
            }

            for(Brand__c brand: lstBrand){
                s.add(new selectoption(brand.Id, brand.Name));
            }
            return s;
        }

        public List<SelectOption> getTypesSelectOptions(String carId){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Type__c type: [SELECT Id, Name FROM Type__c WHERE Car__c=: carId]){
                s.add(new selectoption(type.Id, type.Name));
            }
            return s;
        }

        public List<SelectOption> getYearsSelectOptions(String carId){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Year__c year: [SELECT Id, Name FROM Year__c WHERE Car__c=: carId]){
                s.add(new selectoption(year.Id, year.Name));
            }
            return s;
        }        
    }
}

Screenshot:

enter image description here

Update : Delete the selected values from first picklist

To delete the selected values from the first picklist. You have to update getCarsSelectOptions method. Add the cars to the list only when it is not selected in any of the items. Also, update the updateRow method to update the cars SelectOption list.

Updated Code:

VF Page

<apex:page controller="MyController5" >
    <apex:form >
        <apex:pageBlock title="My Content">
            <apex:pageBlockTable value="{!items}" var="item" id="itemsTable">
                <apex:column headerValue="Car">
                    <apex:selectList value="{!item.selectedCarId}" size="1" id="carSelect" >
                        <apex:selectOptions value="{!item.cars}"/>
                        <apex:actionSupport action="{!updateRow}" event="onchange" reRender="itemsTable" status="addnew" />
                    </apex:selectList>
                </apex:column>
                <apex:column headerValue="Brand">
                    <apex:selectList value="{!item.selectedBrandId}" size="1" id="brandSelect">
                        <apex:selectOptions value="{!item.brands}"/>
                        <apex:actionSupport event="onchange" reRender="typeSelect" status="addnew"/>
                    </apex:selectList>
                </apex:column>

                <apex:column headerValue="Type">
                    <apex:selectList value="{!item.selectedTypeId}" size="1" id="typeSelect">
                        <apex:selectOptions value="{!item.types}"/>
                        <apex:actionSupport event="onchange" reRender="yearSelect" status="addnew"/>
                    </apex:selectList>
                </apex:column>

                <apex:column headerValue="Year">
                    <apex:selectList value="{!item.selectedYeardId}" size="1" id="yearSelect">
                        <apex:selectOptions value="{!item.years}"/>
                    </apex:selectList>
                </apex:column>                
            </apex:pageBlockTable>
            <apex:actionStatus id="addnew">
                <apex:facet name="stop">
                    <apex:outputPanel >
                        <apex:commandButton value="Add new" action="{!addNew}" reRender="itemsTable"/>
                        <apex:commandButton value="Show Selection" reRender="showsel"/>
                    </apex:outputPanel>
                </apex:facet>
                <apex:facet name="start">
                    <apex:outputPanel >
                        <apex:commandButton value="Add new" disabled="true"/>
                        <apex:commandButton value="Show Selection" disabled="true"/>                
                    </apex:outputPanel>
                </apex:facet>
            </apex:actionStatus>            
            <apex:pageBlockTable value="{!items}" var="item" id="showsel">
                <apex:column value="{!item.selectedCarId}" headerValue="Car"/>
                <apex:column value="{!item.selectedBrandId}" headerValue="Brand"/>
                <apex:column value="{!item.selectedTypeId}" headerValue="Type"/>
                <apex:column value="{!item.selectedYeardId}" headerValue="Year"/>               
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Controller

public class MyController5 {

    public List<CarWrapper> items {get;set;}    

    public MyController5() {
        items = new List<CarWrapper>();
        CarWrapper cw = new CarWrapper(this);
        items.add(cw);
    }
    public PageReference addNew(){
        if(items != null){
            CarWrapper cw = new CarWrapper(this);
            items.add(cw);
        }
        return null;
    }    
    public PageReference updateRow(){
        String selectedCarId;
        for(CarWrapper carwrp:items){
            if(String.isNotBlank(carwrp.selectedCarId)){
                selectedCarId = carwrp.selectedCarId;
                carwrp.brands = carwrp.getBrandSelectOptions(selectedCarId);
                carwrp.types = carwrp.getTypesSelectOptions(selectedCarId);
                carwrp.years = carwrp.getYearsSelectOptions(selectedCarId);                
            }else{
                carwrp.cars =  carwrp.getCarsSelectOptions();
            }
        }
        return null;
    }

    public class CarWrapper{

        public String selectedCarId{get; set;}
        public String selectedBrandId{get; set;}
        public String selectedTypeId{get; set;}
        public String selectedYeardId{get; set;}

        public List<SelectOption> cars {get; set;}
        public List<SelectOption> brands{get; set;}
        public List<SelectOption> types{get; set;}
        public List<SelectOption> years{get; set;}        

        Map<Id, List<Brand__c>> carBrandMap = new Map<Id, List<Brand__c>>();

        MyController5 outerCls;

        public CarWrapper(MyController5 outerCls){
            this.outerCls = outerCls;
            this.cars = getCarsSelectOptions();
            this.brands = new list<selectoption> { new selectoption('','Select one') };
            this.types = new list<selectoption> { new selectoption('','Select one') };
            this.years = new list<selectoption> { new selectoption('','Select one') };            
        }
        public List<SelectOption> getCarsSelectOptions(){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));
            Boolean carPresent = false;
            for(Car__c car: [SELECT Id, Name FROM Car__c]){
                carPresent = false;
                for(CarWrapper carwrp:outerCls.items){
                    if(carwrp!=null && carwrp.selectedCarId==car.Id){
                        carPresent = true;
                    }
                }                
                if(!carPresent){
                    s.add(new selectoption(car.Id, car.Name));
                }
            }
            return s;
        }        
        public List<SelectOption> getBrandSelectOptions(String carId){
            List<Brand__c> lstBrand = new List<Brand__c>();

            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            if(carBrandMap.containsKey(carId)){
                lstBrand = carBrandMap.get(carId);
            }else{
                lstBrand = [SELECT Id, Name FROM Brand__c WHERE Car__c=: carId];
                carBrandMap.put(carId,lstBrand);
            }

            for(Brand__c brand: lstBrand){
                s.add(new selectoption(brand.Id, brand.Name));
            }
            return s;
        }

        public List<SelectOption> getTypesSelectOptions(String carId){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Type__c type: [SELECT Id, Name FROM Type__c WHERE Car__c=: carId]){
                s.add(new selectoption(type.Id, type.Name));
            }
            return s;
        }

        public List<SelectOption> getYearsSelectOptions(String carId){
            List<selectoption> s = new list<selectoption> ();
            s.add(new selectoption('','Select one'));

            for(Year__c year: [SELECT Id, Name FROM Year__c WHERE Car__c=: carId]){
                s.add(new selectoption(year.Id, year.Name));
            }
            return s;
        }        
    }
}
| improve this answer | |
  • Thank you @Saroj Bera. That was exactly what I need. I have another question because I was trying to remove selected cars in "updateRow()" method but unfortunately it doesn't work. Could you provide sample of required action. – Adamo Apr 8 '17 at 9:15
  • @Adamo Check my updated answer. And please don't forget to accept the answer if it resolved your issue. :) – Saroj Bera Apr 8 '17 at 17:10
  • Thanks again. Of course I set this answer as best answer becauese you deserve it without a doubt. – Adamo Apr 8 '17 at 19:04
1

Few issues I could see.

There are duplicate variable and method declarations

public List<CarWrapper> items;

And

public List<CarWrapper> getItems(){
    items = new List<CarWrapper>(items2);
    CarWrapper cw = new CarWrapper(selectedCarId);
    items.add(cw);
    return items;
}

Secondly,

you do not need declare duplicate List<CarWrapper> as items and items2. One will serve your purpose.

Thirdly,

Every time you are making database call, when selection is getting changed.

You could put them into the Map and search for that if it is previously exists then retrieve from the map.

Sample Code:

Map<Id, List<Brand__c>> carBrandMap = new Map<Id, Brand__c>();

    public List<SelectOption> getBrandSelectOptions(Id carId){
        List<Brand__c> lstBrand = new List<Brand__c>();

        List<selectoption> s = new list<selectoption> ();
        s.add(new selectoption('','Select one'));

        if(carBrandMap.containsKey(carId))
        {
            List<Brand__c> lstBrand = carBrandMap.get(carId);           
        }
        else
        {
            List<Brand__c> lstBrand = [SELECT Id, Name FROM Brand__c WHERE Car__c=: carId];
            carBrandMap.put(carId,lstBrand);
        }

        for(Brand__c brand: lstBrand){
            s.add(new selectoption(brand.Id, brand.Name));
        }
        return s;
    }

If you can refactor this code probably issue will be gone.

Regarding deletion part, first give a try and put it as a separate question.

| improve this answer | |
  • 1. I do not understand why duplicate? I create the list in which records are stored. – Adamo Apr 4 '17 at 19:41
  • 2. Unfortunately I think I must. Because deleting this line causes problems with add new row functionality. – Adamo Apr 4 '17 at 19:43
  • 3. I agree but the problem still remains unresolved. – Adamo Apr 4 '17 at 19:44
  • Issue still exists in AddNew – Santanu Boral Apr 4 '17 at 19:56
  • I don't understand where ? If you mean items2 list, I wrote this is a copy before adding a new row to the table. – Adamo Apr 5 '17 at 5:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.