0

I've created a VF page that displays 2 tables of contacts. The page contains command links "x" and "+". When X is clicked, it should be removed from the top table and placed on the bottom table. When + is clicked, the reverse should happen.

I have verified the functionality. My issue is that the tables are not re-rendered after the action is performed.

VF Page (updated to show final product since there's no reason to archive broken code):

<apex:page standardController="Case_Contact__c" recordSetVar="passThrough" extensions="CaseContactControllerHelper">
<apex:form id="form">
    <apex:pageBlock id="pageBlock" title="Add Multiple Case Contacts">
        <apex:pageBlockButtons >
        </apex:pageBlockButtons>
        <apex:outputPanel id="container">
            <apex:pageBlockSection columns="1" title="Case Contacts"
                id="CCSection" collapsible="False">
                <apex:pageBlockTable value="{!CaseContacts}" var="CaseContact" id="CCTable">
                    <apex:column headerValue="">
                        <apex:commandLink value="X" action="{!DeleteCaseContact}" style="color:red;text-decoration:none;" 
                            rerender="container" immediate="true">
                            <apex:param assignTo="{!ToDo}" value="{!CaseContact.Id}" name="{!CaseContact.Id}"/>
                        </apex:commandLink>
                        <!--<apex:outputText value="X" style="color:red"/>
                            <apex:actionSupport event="onclick" action="{!DeleteCaseContact}" rerender="container">
                                <apex:param assignTo="{!ToDo}" value="{!CaseContact.Id}" name="{!CaseContact.Id}"/>
                            </apex:actionSupport>-->
                    </apex:column>
                    <apex:column headerValue="First Name">
                        <apex:inputField value="{!CaseContact.First_Name__c}" style="width:100px;" rendered="{!ISNULL(CaseContact.Contact__c)}"/>
                        <apex:outputField value="{!CaseContact.First_Name__c}" style="width:100px;" rendered="{!NOT(ISNULL(CaseContact.Contact__c))}" />
                    </apex:column>
                    <apex:column headerValue="Last Name">
                        <apex:inputField value="{!CaseContact.Last_Name__c}" style="width:100px;" rendered="{!ISNULL(CaseContact.Contact__c)}"/>
                        <apex:outputField value="{!CaseContact.Last_Name__c}" style="width:100px;" rendered="{!NOT(ISNULL(CaseContact.Contact__c))}" />
                    </apex:column>
                    <apex:column headerValue="Email">
                        <apex:inputField value="{!CaseContact.Email__c}" style="width:200px;" rendered="{!ISNULL(CaseContact.Contact__c)}"/>
                        <apex:outputField value="{!CaseContact.Email__c}" style="width:200px;" rendered="{!NOT(ISNULL(CaseContact.Contact__c))}" />
                    </apex:column>
                </apex:pageBlockTable>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Account Contacts"
                id="ACSection" collapsible="False">
                <apex:pageBlockTable value="{!AccountContacts}" var="AccountContact" id="ACTable">
                    <apex:column headerValue="">
                        <apex:commandLink value="+" action="{!CreateCaseContact}" style="color:Blue;text-decoration:none;" 
                            rerender="container" immediate="true">
                            <apex:param assignTo="{!ToDo}" value="{!AccountContact.Id}" name="{!AccountContact.Id}"/>
                        </apex:commandLink>
                    </apex:column>
                    <apex:column headerValue="First Name">
                        <apex:outputField value="{!AccountContact.FirstName}" style="width:100px;"/>
                    </apex:column>
                    <apex:column headerValue="Last Name">
                        <apex:outputField value="{!AccountContact.LastName}" style="width:100px;"/>
                    </apex:column>
                    <apex:column headerValue="Email">
                        <apex:outputField value="{!AccountContact.Email}" style="width:200px;"/>
                    </apex:column>
                </apex:pageBlockTable>
            </apex:pageBlockSection>
        </apex:outputPanel>
    </apex:pageBlock>
</apex:form>
</apex:page>

Controller Extension (updated to show final product since there's no reason to archive broken code):

public class CaseContactControllerHelper {
    private Case Parent;
    public List<Case_Contact__c> CaseContacts;
    public List<Contact> AccountContacts;
    public List<Id> Exclude;
    public Id ToDo {get;set;}

    public CaseContactControllerHelper(apexPages.StandardSetController stdCon) {
        Parent = [SELECT Id, AccountId FROM Case 
            WHERE Id=:ApexPages.currentPage().getParameters().get('id')];
        setCaseContacts();
        setAccountContacts();
    }

    public pagereference DeleteCaseContact() {
        Delete [SELECT Id FROM Case_Contact__c WHERE Id = :ToDo];
        setCaseContacts();
        setAccountContacts();
        return Null;
    }

    public pagereference CreateCaseContact() {
        INSERT new Case_Contact__c(Case__c=Parent.Id,Contact__c=ToDo);
        setCaseContacts();
        setAccountContacts();
        return Null;
    }

    private void setAccountContacts() {
        this.AccountContacts = [SELECT FirstName, LastName, Email, Phone FROM Contact
            WHERE AccountId = :Parent.AccountId AND Id NOT IN :exclude];
    }

    public List<Contact> getAccountContacts() {
        return this.AccountContacts;
    }

     private void setCaseContacts() {
        this.CaseContacts = [SELECT First_Name__c, Last_Name__c, Email__c, Phone__c, contact__c 
            FROM Case_Contact__c WHERE Case__c = :Parent.Id];
        exclude = new List<Id>();
        for (Case_Contact__c cc : CaseContacts) {
            if (cc.contact__c != Null) {
                exclude.add(cc.contact__c);
            }
        }
        system.debug(exclude);
    }

    public List<Case_Contact__c> getCaseContacts() {
        return this.CaseContacts;
    }
}

More information: The Case_Contact__c object is a custom junction object that allows me to associate more then 1 contact to a case. When the X is clicked, the associated Case Contact record should be deleted. When the + is clicked next to a contact, a new case contact record should be created.

I have verified this functionality. The only thing I cannot seem to get working is the output panel (container) to rerender.

Update:

Code snippet after applying Alex's suggestion:

<apex:column headerValue="">
    <!--<apex:commandLink value="X" action="{!DeleteCaseContact}" style="color:red;text-decoration:none;" 
        rerender="container" immediate="true">
        <apex:param assignTo="{!ToDo}" value="{!CaseContact.Id}" name="{!CaseContact.Id}"/>
    </apex:commandLink>-->
    <apex:outputText value="X" style="color:red"/>
        <apex:actionSupport event="onclick" action="{!DeleteCaseContact}" rerender="container">
            <apex:param assignTo="{!ToDo}" value="{!CaseContact.Id}" name="{!CaseContact.Id}"/>
        </apex:actionSupport>
    <apex:variable var="rowCount" value="{!rowCount + 1}"/>
</apex:column>

Unfortunately this did not work either. It was able to delete the case contact record as desired but the page did not update.

2
  • as an aside - you don't have apex:pageMessages and you may be masking/hiding any errors that occur when the action methods execute - this can be a common problem when rerendering isn't happening as expected; also, your + and x action methods should return null
    – cropredy
    Feb 4, 2016 at 2:01
  • @crop1645 They do return null, that was just a last ditch effort to try to force the page to refresh. As for the apex:pageMessages, the apex code does exactly what it's suppose to do. If there is an error it's not going to be visible with apex:pageMessages.
    – gNerb
    Feb 4, 2016 at 2:06

1 Answer 1

1

For "AJAX" style functionality, I've always had more success with apex:actionsupport or apex:actionfunction, than with apex:commandlink. I've only ever been able to get commandlinks to work where the method returns a pagereference, which is not what you would want in this case.

I recommend you change from <apex:commandlink> to <apex:outputtext>, and then nest an <apex:actionsupport> to call the action and rerender the outputpanel.

The documentation for apex:actionfunction (https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_compref_actionSupport.htm) gives a good example of how to achieve this:

   <apex:outputpanel id="counter">
        <apex:outputText value="Click Me!: {!count}"/>
        <apex:actionSupport event="onclick" 
                            action="{!incrementCounter}" 
                            rerender="counter" status="counterStatus"/>
    </apex:outputpanel>
    <apex:actionStatus id="counterStatus" 
                       startText=" (incrementing...)" 
                       stopText=" (done)"/>

EDIT: Your CreateCaseContact and DeleteCaseContact also need to refresh the CaseContacts variable after inserting/deleting the Contact. At the moment that variable is only populated in the Constructor Method. When the outputpanel refreshes, it is refreshing from the same data.

So you could edit the CreateCaseContact method to this (and the DeleteCaseContact similarly) and it should work as you want:

public pagereference CreateCaseContact() {
    system.debug('Hello ' + ToDo);
    INSERT new Case_Contact__c(Case__c=Parent.Id,Contact__c=ToDo);

    CaseContacts = [SELECT Contact__c, Case__c, First_Name__c, Last_Name__c, Email__c, Phone__c FROM Case_Contact__c WHERE Case__c = :Parent.Id];

    return Null;
}
6
  • BTW - apex:param an also be nested within the actionsupport tag to set your parameter. Feb 3, 2016 at 23:10
  • Unfortunately, your suggestion didn't work. To be honest the phrase "I've always had more luck" is a bit frustrating; nothing on you, rather, the fact this is so inconsistent that developers are relying on luck.
    – gNerb
    Feb 4, 2016 at 2:14
  • OK, I've looked at your controller a bit closer and found a problem. Your two action methods in the controller (CreateCaseContact and DeleteCaseContact) need to re-populate the CaseContacts variable. I'll do another answer to go through it in more detail. Feb 4, 2016 at 2:22
  • That works, almost; the account list isn't updating. I imagine it's the same situation. This bugs me still, the get method for the account list has the SQL query built in. To me, that shows that the list should be updating without the need for me to do it specifically does it not? (even removing the conditional so that it always runs the query doesn't seem to work)
    – gNerb
    Feb 4, 2016 at 2:43
  • The get method will populate it initially, but it won't automatically update it as the underlying data changes. I guess potentially you could re-call the getter from the insert/delete methods, although I've never seen that done. Feb 4, 2016 at 2:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .