function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
BeautifulDrifterBeautifulDrifter 

Help with Updating related records in Pageblocktable

Ok...so I am relatively new to this and have hit a wall.  That being said, I have tried this a few way.  I'm sure this will end up being something minor. 

Basically, I am creating a questionaire, and breaking the questions out by category.  I have the data and have it rendering correctly in VF.  The issue is with trying to update the answers that are entered.  Also, please note that the answers are dependent picklists.  Below is an image of the page, and my two primary attempts at this.  You may see some random extra code/buttons from me trying different things to update the data.  I just need to update records with the entered answers within the VF page.

Questionaire


First try:

APEX:
public class CatDeetsEx {
    
    Public List<PSQ_Category__c> CategoriesList {get;set;}
    Public List<PSQ_Detail__c> DetailsList {get;set;}
    
    
    private ApexPages.StandardController stdCtrl {get; set;}
    public CatDeetsEx(ApexPages.StandardController std)
        
    {
        stdCtrl=std;
        setDeetsList();
        
    }
Private void SetDeetsList()
    {
        CategoriesList = [Select Id,Name, (select Id,PSQ_Category__c,PSQ_Header__c,Pick_List_Type__c ,Answer__c,Question__c from PSQ_Details__r 
                                          ) from PSQ_Category__c where Id in (select PSQ_Category__c from PSQ_Detail__c where PSQ_Header__c=:stdCtrl.getId())];
        
    }
    Public PageReference saveAll(){
        
        update CategoriesList.PSQ_Details__r;

    }
    
}
VisualForce:
<apex:pageBlock >

                    <apex:repeat value="{!CategoriesList}" var="cl">
                        <apex:pageBlockSection title="{!cl.Name}" id="catSection">
                                                     
                            <apex:pageBlockTable id="detsTable" value="{!cl.PSQ_Details__r}" var="dl">
                                <apex:column value="{!dl.Question__c}" /> 
                                <apex:column >
                                    <apex:inputField value="{!dl.Pick_List_Type__c}"  id="ParentPL" style="display:none" />
                                </apex:column>
                                <apex:column > 
                                    <apex:inputField value="{!dl.Answer__c}" id="deetAnswer" />
                                    <apex:actionSupport event="onchange" action="{!updateAnswer}" rerender="dets"/>
                                    <apex:param assignTo="{!RecToUpd}" value="{!dl.Id}"/>
                                    <apex:param assignTo="{!updAnswer}" value="{!dl.Answer__c}"/>
                                </apex:column>
                            </apex:pageBlockTable>
                        </apex:pageBlockSection> 
                        <apex:commandButton value="Save" action="{!saveAll}" reRender="catBlockSection"/>
                    </apex:repeat>
                </apex:pageBlock>
Error:  Initial term of field expression must be a concrete SObject: List<PSQ_Category__c>

Ok Ok.  Got it, Salesforce isn't going to make this simple.  So, I do a little research, and the natural first response to a post like this would be WRAPPER CLASS! 
So, I went and wrote (copy/paste/tweaked) a wrapper, it renders correctly, but I can't figure out how to update the values.  See below. 

APEX:
public class CatWrapExt {
    
    private ApexPages.StandardController stdCtrl {get; set;}
    public CatWrapExt(ApexPages.StandardController std)
    {
        stdCtrl=std;
        CatWrapExt();
    }       
    
    
    private List<PSQ_Category__c> tempcat = new List<PSQ_Category__c>();
    private List<PSQ_Detail__c> tempD = new List<PSQ_Detail__c>();
    private List<PSQ_Question__c> tempQ = new List<PSQ_Question__c>();    
    private Map<ID,List<PSQ_Detail__c>> catdetMAP = new Map<ID,List<PSQ_Detail__c>>();
    private Set<ID> catIDs = new Set<ID>();//category ids to get details from
    public List<wrapper> wrapout {get; set;}
    //constructor
    public void CatWrapExt(){
        wrapout = new List<wrapper>();
    }   
    //wrapper 1
    class wrapper{
        public PSQ_Category__c cat {get; set;}
        //List of wrappers
        public List<wrapper2> dets {get; set;}
        
        public wrapper(){
            if(cat==null){cat = new PSQ_Category__c();}//initialize the category holder
            if(dets==null){dets = new List<wrapper2>();}//initialize the wrapper listholder
 
        }
    }
    //wrapper 2 - the sub-wrapper
    class wrapper2{
        public PSQ_Detail__c det {get; set;}
        Public String Answer {get;set;}
        
        //public List<Product2> detprods {get; set;}
        public wrapper2(PSQ_Detail__c det,string Answer
        ){
            if(det==null){det = new PSQ_Detail__c();}//initialize the Detail holder
            
        }
      public void updateDets(){
          //list<psq_detail__c> detUpdate = New List <psq_detail__c>();
          update det;
            }   
        
    }
    
    public PageReference buildwrapper() {
        tempcat = [select id,name from PSQ_Category__c where Id in (Select PSQ_Category__c from PSQ_Record_Category__c 
                                                                    where PSQ_Header__c = :stdCtrl.getId() AND Need__c = True) ];
        for(PSQ_Category__c c:tempcat){catIDs.add(c.id);}
        tempD = [select Id,Detail_Question__c,Question__c,Pick_List_Type__c,Answer__c from PSQ_Detail__c 
                 where PSQ_Header__c =:stdCtrl.getId()];
        for(PSQ_Detail__c d: TempD){
            tempQ = [select Id,PSQ_Category__c from PSQ_Question__c where Id = :d.Detail_Question__c];
            for (PSQ_Question__c q: TempQ){
                if(catdetMap.containsKey(q.PSQ_Category__c)){
                    catdetMap.get(q.PSQ_Category__c).add(d);//adds detail for this category to the det list in the map
                }else{
                    catdetMap.put(q.PSQ_Category__c,new List<PSQ_Detail__c>{d});//adds new detail list for this category to the map
                }
            }
            
        }
        for(PSQ_Category__c cc:tempcat){
            wrapper tmpwrapper = new wrapper();
            tmpwrapper.cat=cc;
            List<wrapper2> t2 = new List<wrapper2>();
            for(PSQ_Detail__c dd:catdetMap.get(cc.id)){
                wrapper2 twrap2 = new wrapper2(dd ,dd.Answer__c);
                twrap2.det=dd;
                twrap2.Answer=dd.Answer__c;
                t2.add(twrap2);
                  
            }
            tmpwrapper.dets=t2;
            wrapout.add(tmpwrapper);

        }
        
        return null;
        
    }
}

Visualforce:
<apex:repeat value="{!wrapout}" var="w">
                        <apex:pageBlockSection title="{!w.cat.name}" id="catBlockSection">
                            <hr/>
                            <apex:outputText value="" /> 
                            <apex:pageBlockTable id="detsBT" value="{!w.dets}" var="wd">
                                <apex:column value="{!wd.det.Question__c}" /> 
                                <apex:column >
                                    <apex:inputField value="{!wd.det.Pick_List_Type__c}"  id="ParentPicklist" style="display:none" />
                                </apex:column>
                                <apex:column > 
                                    <apex:selectList value="{!wd.Answer}" id="answer" />
                                    <apex:selectOptions value="{!level1items}"/>
                                    <apex:actionSupport event="onblur" action="{!updateAnswer}" rerender="dets"/>
                                    <apex:param assignTo="{!RecToUpd}" value="{!wd.det.Id}"/>
                                    <apex:param assignTo="{!updAnswer}" value="{!wd.det.Answer__c}"/>
                                </apex:column>
                            </apex:pageBlockTable>
                        </apex:pageBlockSection> 
                        <apex:commandButton value="Save" action="{!updateTableAnswers}" reRender="catBlockSection"/>
                    </apex:repeat>
For the wrapper, the issue is I can't update the answers without defining them in the constructor.  But if I define them in the constructor and then reference them in the VF page, the drop down does not render since the type string. 

<apex:selectList value="{!wd.Answer}" id="answer" />  | Doesn't render as picklist

<apex:selectList value="{!wd.det.Answer__c}" id="answer" />  | Can't figure out how to update the value

I also tried actionsupport and passing params from the page for both onchange and onblur.  I feel like this has become way more complicated than it should be.  The one thing I DO NOT want to do is put a save button on each individual line of the table so every time someone answers a question they then have to save it. 

Any help would be imensely appreciated as this is a sticking point that I need to work to move forward with what I am building.

Thanks in advance!

 
Best Answer chosen by BeautifulDrifter
Nayana KNayana K
Oh yes, you are correct I was facing same issue in one of my project....SO this is what I did...

<apex:selectList value="{!strSelectedProductId}" size="1" id="productsSL" disabled="{!!ISNULL(objCafe.Id)}" onchange="assignCafePrice(this.value);">
                        <apex:selectOptions value="{!lstProductsByCategory}"/>
                    </apex:selectList>

and action function:

 <apex:actionFunction name="assignCafePrice" action="{!assignCafePrice}" status="status" reRender="price, pgMsgs" immediate="true">
            <apex:param name="selectedProduct" value="" assignTo="{!strSelectedProductId}"/>
        </apex:actionFunction>


So you can try something like this:


<apex:inputField value="{!dl.Answer__c}" onchange=dummyMethod( '{!dl.Id}',this.value); > </apex:inputField>

actionFunction:
<apex:actionFunction name="dummyMethod" action="{!updateA}"  reRender="dets" immediate="true">
<apex:param name = "p1" assignTo="{!RecordToUpdate}" value=""/> <apex:param name = "p2" assignTo= "{!updatedAnswer}" value=""/>
</apex:actionFunction>

All Answers

Nayana KNayana K
I hope Answer__c is picklist field.
 <apex:inputField value="{!wd.det.Answer__c}">
    <apex:actionSupport event="onchange" 
                                action="{!updateAnswer}" 
                                rerender="dets">
        <apex:param name="p1" assignTo="{!RecToUpd}" value="{!wd.det.Id}"/>
        <apex:param name="p2" assignTo="{!updAnswer}" value="{!wd.det.Answer__c}"/>
    </apex:actionSupport>
</apex:inputField>

public String RecToUpd {get;set;}
public String updAnswer {get;set;}
public void updateAnswer()
{
    system.debug('==RecToUpd=='+RecToUpd);
    system.debug('==updAnswer=='+updAnswer);
    PSQ_Detail__c objDet = new PSQ_Detail__c(Id = RecToUpd, Answer__c = updAnswer);
    update objDet;
}

Not sure whether above works, but you can give a try.
BeautifulDrifterBeautifulDrifter
I tried this previously and could not get it to work.  I added it again, and after further investigation within the logs, it seems that the issue is the Param is actually sending the old value, not the new value.

I changed the answer on one of the questions from "Yes" to "No".  For some reason it is pulling the old value, not the new one.  Any more help on this would be great.

15:05:57:085 USER_DEBUG [55]|DEBUG|==RecToUpdate==a0G41000003evjrEAA
15:05:57:085 USER_DEBUG [56]|DEBUG|==updatedAnswer==Yes
 
<apex:inputField value="{!dl.Answer__c}" id="deetAnswer" >
                                        <apex:actionSupport event="onblur" action="{!updateA}" rerender="dl">
                                            <apex:param name = "p1" assignTo="{!RecordToUpdate}" value="{!dl.Id}"/>
                                            <apex:param name = "p2" assignTo= "{!updatedAnswer}" value="{!dl.Answer__c}"/>
                                        </apex:actionSupport>
                                    </apex:inputField>
 
public void updateA()
    {
        system.debug('==RecToUpdate=='+RecordToUpdate);
        system.debug('==updatedAnswer=='+updatedAnswer);
        PSQ_Detail__c objDet = new PSQ_Detail__c(Id = RecordToUpdate, Answer__c = updatedAnswer
                                                );
        update objDet;
        
    }


 
BeautifulDrifterBeautifulDrifter
Also, I was using onchange, not onblur.  I just changed it to see if that would make a difference.  It didn't.
Nayana KNayana K
Oh yes, you are correct I was facing same issue in one of my project....SO this is what I did...

<apex:selectList value="{!strSelectedProductId}" size="1" id="productsSL" disabled="{!!ISNULL(objCafe.Id)}" onchange="assignCafePrice(this.value);">
                        <apex:selectOptions value="{!lstProductsByCategory}"/>
                    </apex:selectList>

and action function:

 <apex:actionFunction name="assignCafePrice" action="{!assignCafePrice}" status="status" reRender="price, pgMsgs" immediate="true">
            <apex:param name="selectedProduct" value="" assignTo="{!strSelectedProductId}"/>
        </apex:actionFunction>


So you can try something like this:


<apex:inputField value="{!dl.Answer__c}" onchange=dummyMethod( '{!dl.Id}',this.value); > </apex:inputField>

actionFunction:
<apex:actionFunction name="dummyMethod" action="{!updateA}"  reRender="dets" immediate="true">
<apex:param name = "p1" assignTo="{!RecordToUpdate}" value=""/> <apex:param name = "p2" assignTo= "{!updatedAnswer}" value=""/>
</apex:actionFunction>
This was selected as the best answer
BeautifulDrifterBeautifulDrifter
<apex:pageBlock id="DiscoveryBlock">
                <apex:repeat value="{!CategoriesList}" var="cl">
                    <apex:commandButton value="Set Default Answers" action="{!setDefaultsForCategory}" reRender="DiscoveryBlock">
                        <apex:param name="defCat" value="{!cl.Id}" assignTo="{!CatForDefaults}"/>
                    </apex:commandButton>
                    <apex:pageBlockSection title="{!cl.Name}" id="catSection">
                        <apex:pageBlockTable id="detsTable" value="{!cl.PSQ_Details__r}" var="dl">
                            <apex:column value="{!dl.Id}" />
                        <apex:column value="{!dl.Question__c}" />
                        <apex:column >
                            <apex:inputField value="{!dl.Pick_List_Type__c}"  id="ParentPL" style="display:none" />
                        </apex:column>
                        <apex:column >
                            <apex:inputField value="{!dl.Answer__c}" onchange="dummyMethod('{!dl.Id}',this.value);" id="deetAnswer" >
                                <!--<apex:actionSupport event="onchange" action="{!updateA}" reRender="dl">-->
                                <!--<apex:param name="p1" assignTo="{!RecordToUpdate}" value="{!dl.Id}"/>-->
                                <!--<apex:param name="p2" assignTo="{!updatedAnswer}" value="this.value"/>-->
                                <!--</apex:actionSupport>-->
                            </apex:inputField>
                        </apex:column>
                    </apex:pageBlockTable>
                </apex:pageBlockSection>
                <apex:commandButton value="Save" action="{!save}" reRender="catBlockSection" rendered="False"/>
            </apex:repeat>
            <apex:actionFunction name="dummyMethod"  action="{!updateA}" >
                <apex:param name="p1" assignTo="{!RecordToUpdate}" value=""/>
                <apex:param name="p2" assignTo="{!updatedAnswer}" value=""/>
            </apex:actionFunction>
        </apex:pageBlock>

I think I am almost there.  Now I am getting a NULL error for the Id.  It isn't passing anything to the new method for the Id.  Let me know if you can help!  Again, thanks in advance.

 
Nayana KNayana K
I am not understanding why this is happening...

So what about picklist value, is it coming proper?

Make sure RecordToUpdate is STring and public get set
 
BeautifulDrifterBeautifulDrifter

Ok...so I got it working.  So, I guess it needed a rerender for some reason.  Without the rerender it didn't pass the variable correctly.  Weird.  Anyways, Thanks for your help.  Below is the code, and I didn't have to use a wrapper which was a plus. 


 
<apex:pageBlock id="DiscoveryBlock">
                <apex:repeat value="{!CategoriesList}" var="cl">
                    <apex:commandButton value="Set Default Answers" action="{!setDefaultsForCategory}" reRender="DiscoveryBlock">
                        <apex:param name="defCat" value="{!cl.Id}" assignTo="{!CatForDefaults}"/>
                    </apex:commandButton>
                    <apex:pageBlockSection title="{!cl.Name}" id="catSection">
                        <apex:pageBlockTable id="detsTable" value="{!cl.PSQ_Details__r}" var="dl">
                            <apex:column value="{!dl.Id}" />
                        <apex:column value="{!dl.Question__c}" />
                        <apex:column >
                            <apex:inputField value="{!dl.Pick_List_Type__c}"  id="ParentPL" style="display:none" />
                        </apex:column>
                        <apex:column >
                            <apex:inputField value="{!dl.Answer__c}" onchange="dummyMethod('{!dl.Id}',this.value);" id="deetAnswer" >
                                <!--<apex:actionSupport event="onchange" action="{!updateA}" reRender="dl">-->
                                <!--<apex:param name="p1" assignTo="{!RecordToUpdate}" value="{!dl.Id}"/>-->
                                <!--<apex:param name="p2" assignTo="{!updatedAnswer}" value="this.value"/>-->
                                <!--</apex:actionSupport>-->
                            </apex:inputField>
                        </apex:column>
                    </apex:pageBlockTable>
                </apex:pageBlockSection>
                <apex:commandButton value="Save" action="{!save}" reRender="catBlockSection" rendered="False"/>
            </apex:repeat>
            <apex:actionFunction name="dummyMethod"  action="{!updateA}" rerender="DiscoveryBlock">
                <apex:param name="p1" assignTo="{!RecordToUpdate}" value=""/>
                <apex:param name="p2" assignTo="{!updatedAnswer}" value=""/>
            </apex:actionFunction>
        </apex:pageBlock>
 
Public void SetDeetsList()
    {
        CategoriesList = [Select Id,Name, (select Id,PSQ_Category__c,PSQ_Header__c,Pick_List_Type__c ,Answer__c,Question__c from PSQ_Details__r 
                                          ) from PSQ_Category__c where Id in (select PSQ_Category__c from PSQ_Detail__c where PSQ_Header__c=:stdCtrl.getId())];
        
        DetailsList = [select Id,Name,Question__c,Pick_List_Type__c,Answer__c,PSQ_Category__c, PSQ_Header__c,PSQ_Category__r.Name from PSQ_Detail__c where PSQ_Header__c=:stdCtrl.getId()];
    }
     Public PageReference saveAll(){
        
        //update CategoriesList.PSQ_Details__r;
        
        
        //PSQ_Detail__c UpdateDetails = New PSQ_Detail__c(Id = CategoriesList.PSQ_Details__r);
        
        return null;
     }            
    public string RecordToUpdate {get; set;}
    public string updatedAnswer {get; set;}
    
    
    public void updateA()
    {
        system.debug('==RecToUpdate=='+RecordToUpdate);
        system.debug('==updatedAnswer=='+updatedAnswer);
        PSQ_Detail__c objDet = new PSQ_Detail__c(Id = RecordToUpdate, Answer__c = updatedAnswer
                                                );
        update objDet;