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
TLFTLF 

Help with selection of a row in a data table

Hello, I have a requirement to display a table of contacts and to allow the user to select one, and only one row from the table. I also want to use AJAX to rerender a different part of the page to display information about the selected contact when the user selects it. I have created a data table with an inputCheckbox column. I have also created an Apex class called "SelectableContact" which serves as a wrapper for each Contact, with a Boolean "selected" field. This is what I have so far:
 
The page:
 
Code:
<apex:page controller="ProcessingController" >
    <apex:detail />
        <apex:pageBlock id="contactHandlingBlock" title="Contact Handling" >
            <apex:form id="contactHandlingForm" >            
                <apex:pageBlockSection title="Similar Contacts" >
                    <apex:panelGrid columns="1" >                
                            <apex:dataTable id="simContactsTable" value="{!similarContacts}" var="simContact" cellpadding="4" border="1" >
                                <apex:column >
                                    <apex:facet name="header">Select</apex:facet>
                                    <apex:inputCheckbox id="select" value="{!simContact.selected}" onclick="deselectOther(this)" >
                                        <apex:actionSupport event="onclick" rerender="debugText" />
                                    </apex:inputCheckBox>
                                </apex:column>
                                <apex:column >
                                    <apex:facet name="header">Name</apex:facet>
                                    <apex:outputText value="{!simContact.contact.name}"/>
                                </apex:column>
                                <apex:column >
                                    <apex:facet name="header">Last Name</apex:facet>
                                    <apex:outputText value="{!simContact.contact.lastname}"/>
                                </apex:column>
                                <apex:column >
                                    <apex:facet name="header">Street Address</apex:facet>
                                    <apex:outputText value="{!simContact.contact.mailingstreet}"/>
                                </apex:column>
                                <apex:column >
                                    <apex:facet name="header">City</apex:facet>
                                    <apex:outputText value="{!simContact.contact.mailingcity}"/>
                                </apex:column>
                                <apex:column >
                                    <apex:facet name="header">State</apex:facet>
                                    <apex:outputText value="{!simContact.contact.mailingstate}"/>
                                </apex:column>
                                <apex:column >
                                    <apex:facet name="header">Email</apex:facet>
                                    <apex:outputText value="{!simContact.contact.email}"/>
                                </apex:column>
                            </apex:dataTable>
                            <apex:selectRadio id="contactOptions" value="{!selectedOption}" onclick="submit();" immediate="true" >
                                <apex:selectOption id="create" itemValue="create_new" itemLabel="Create new Contact" />
                                <apex:selectOption id="merge" itemValue="merge_with_selected" itemLabel="Merge with selected Contact" />
                            </apex:selectRadio>
                    </apex:panelGrid>                
                </apex:pageBlockSection>
            </apex:form>                    
        </apex:pageBlock>
        
            
    <apex:outputText id="debugText" value="{!selectedContact.name}" />
    
    <script>          
        var selectedChkbox;
        
        function deselectOther(chkBox) {
            if (chkBox.checked) {
                if ((chkBox != selectedChkbox) && (selectedChkbox != null)) {
                    selectedChkbox.checked = false;
                }
                selectedChkbox = chkBox;
            }            
        }
    </script>     
</apex:page>

 My Controller:
 
Code:
public class ProcessingController {

    List<SelectableContact> similarContacts = null;
    
    public Web_Donation__c getWebDonation() {
        Web_Donation__c webDonation = [select id, name, last_name__c, address__c, city__c, state__c, email__c 
            from web_donation__c where id = :getWebDonationId()]; 
        return webDonation;
    }
    
    /* Returns a list of contacts with same last name, or same street address and city, or same 
     * email address as the web donation object being processed
     */
    public List<SelectableContact> getSimilarContacts() {
        
        if (similarContacts == null) {
        
            similarContacts = new List<SelectableContact>();
        
            // Get the personal info from the web donation object
            Web_Donation__c webDonation = getWebDonation();
                    
            // Query for similar contacts by last name, address, email.
            List<Contact> contacts = 
                [select id, name, lastname, mailingstreet, mailingcity, mailingstate, email from contact where 
                    (lastname = :webDonation.last_name__c) or 
                    ((contact.mailingstreet = :webDonation.address__c) and 
                     (contact.mailingcity = :webDonation.city__c) and 
                     (contact.mailingstate = :webDonation.state__c)) or
                    (contact.email = :webDonation.email__c)];        
         
            // Build the list of similar contacts           
            for (Contact contact : contacts) {
                SelectableContact similarContact = new SelectableContact(contact);
                similarContacts.add(similarContact);
            }
        }        
        return similarContacts;
    }

    public void setSimilarContacts(List<SelectableContact> s) {
        similarContacts = s;
    }

    /*
     * Returns the selected Contact
     */
    public Contact getSelectedContact() {
        Contact selectedContact = null;
        if (similarContacts != null) {
         
             for (SelectableContact similarContact : similarContacts) {
                 if (similarContact.getSelected()) {
                     selectedContact = similarContact.getContact();
                 }
             }           
        }
        return selectedContact;
    }    
}

 
The SelectableContact class:
 
Code:
public class SelectableContact {

    private boolean selected = false;
    private Contact contact = null;
    
    public SelectableContact() {
    }
    
    public SelectableContact(Contact c) {
        contact = c;
    }
    
    public Contact getContact() {
        return contact;
    }
    
    public void setContact(Contact c) {
        contact = c;
    }
    
    public boolean getSelected() {
        return selected;
    }
    
    public void setSelected(boolean s) {
        selected = s;
    }    
}

 
For now, all I am trying to do is display the name of the selected contact in the outputText component with the ID "debugText". I've attached an actionSupport component to the inputCheckbox and rerender the debugText component when the checkbox is clicked. The Javascript function handles single-row selection. However, I am not seeing the selected contact name. I suspect this is because the data table does not get submitted back to the controller, and therefore the List<SelectableContact> is not updated with the selection. Anyone have a suggestion as to how to tackle this problem? Thanks in advance.
Best Answer chosen by Admin (Salesforce Developers) 
GoForceGoGoForceGo
The hack below works...

I am not sure if there is a better way to do this.

I am using thePage:theForm:j_id2 below....even $Component.theForm.theTable doesn't work - only $Component.theForm works.

Seems like a bad hack...I am not sure if i can rely on j_id2 being the same for ever - when I modified some code, it ended up being j_id1.

Is there  a way of accessing the table component without this hack?

In the code below, I am using Javascript to make sure only one row can be marked primary distributor. The code where I am trying to create the element id sets the checkbox that was selected at initially (selectChkBox) . deSelectOther allows only one to be selected from there on...

Code:
<apex:page controller="selectPreferredDistributorsController" tabStyle="Distributor__c" id="thePage">
<script>
var selectedChkbox;
var tableIdCount = 0;
function deSelectOthers(chkBox) {
if (chkBox.checked) {
if ((chkBox != selectedChkbox) && (selectedChkbox != null)) {
selectedChkbox.checked = false;
}
selectedChkbox = chkBox;
}
}
</script>
<apex:form id="theForm">
<apex:sectionHeader title="Select Preferred Distributors" />
<apex:pageBlock >
<apex:pageBlockButtons >
<apex:commandButton action="{!Save}" value="Save" />
<apex:commandButton action="{!Cancel}" value="Cancel" />
</apex:pageBlockButtons>
<!-- show the Table with selection -->
<apex:pageBlockTable value="{!PreferredDistributorWithSelects}" var="pd" id="theTable" frame="border">
<apex:column headerValue="Select">
<apex:inputCheckBox value="{!pd.selected}">
<apex:actionSupport event="onclick" rerender="primaryCheckBox" action="{!uncheckPrimaryIfChecked}"/>
</apex:inputCheckBox>
</apex:column>
<apex:column value="{!pd.pDistributor.name}" />
<apex:column headerValue="Primary Distributor">
<apex:inputCheckBox value="{!pd.primary}" id="primaryCheckBox" disabled="{!NOT(pd.selected)}" onclick="deSelectOthers(this)" />
<script>
if ("{!pd.primary}" == "true") {
var idForSelectedBox = "thePage:theForm:j_id2:theTable:"+tableIdCount+":primaryCheckBox";
selectedChkbox = document.getElementById(idForSelectedBox);
}
tableIdCount++;
</script>
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:form>
</apex:page>

 











Message Edited by GoForceGo on 12-22-2008 12:45 PM

All Answers

Ron WildRon Wild
I had a similar problem and just happened to stumble on the answer:

The setter for the attribute your are binding to your checkbox isn't called until an action is performed ... apparently the onclick event doesn't make this happen (although it seems as though it should).

If you add an apex:actionSupport component to your checkbox as follows, you might get the results you are looking for.


Code:
   <apex:inputCheckbox id="check" immediate="true" onclick="{!selectableContact.selectedState}" value="{!selectableContact.selected}">
        <apex:actionSupport action="{!nullAction}" event="onclick" />
   </apex:inputCheckbox>   

The controller should have a corresponding nullAction method:

Code:
    public PageReference nullAction() {
     return null; 
    }

 
 Good luck!
 
GoForceGoGoForceGo
I am trying to use the deselectOther javascript function.

My underlying data has a row that was already selected before the table is displayed for the first time (the primary flag). As a result, if I select another row, the original row is NOT deselected. Obviously the "selectedChkBox" variable is not initialized when the table is initially rendered.

Any ideas? I would like to initialize the selectedChkbox variable when the table is first loaded.


Code:
<apex:page controller="selectPreferredDistributorsController" tabStyle="Distributor__c">
    <script>          
        var selectedChkbox;      
        function deSelectOthers(chkBox) {
            if (chkBox.checked) {
                if ((chkBox != selectedChkbox) && (selectedChkbox != null)) {
                    selectedChkbox.checked = false;
                }
                selectedChkbox = chkBox;
            }            
        }         
    </script>  
    <apex:form id="theForm">
        <apex:sectionHeader title="Select Preferred Distributors" />
        <apex:pageBlock >
            <apex:pageBlockButtons >
                <apex:commandButton action="{!Save}" value="Save" />
                <apex:commandButton action="{!Cancel}" value="Cancel" />
            </apex:pageBlockButtons>
            <apex:pageBlockSection Title="Select Preferred Distributors" columns="1">
            <!-- show the Table with selection -->
            <apex:pageBlockTable value="{!PreferredDistributorWithSelects}" var="pd" id="tasktable" frame="border">
                <apex:column headerValue="Select"> 
                    <apex:inputCheckBox value="{!pd.selected}">
                        <apex:actionSupport event="onchange" rerender="primaryCheckBox" />
                    </apex:inputCheckBox>
                </apex:column>               
                <apex:column value="{!pd.pDistributor.name}" />
                 <apex:column headerValue="Primary Distributor"> 
                    <apex:inputCheckBox value="{!pd.primary}" id="primaryCheckBox" disabled="{!NOT(pd.selected)}" onclick="deSelectOthers(this)" />
                </apex:column>
            </apex:pageBlockTable>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
    
</apex:page>

 


GoForceGoGoForceGo
I would also like to replace the checkbox with a radio button....

Is there anyway of doing that?



GoForceGoGoForceGo
The hack below works...

I am not sure if there is a better way to do this.

I am using thePage:theForm:j_id2 below....even $Component.theForm.theTable doesn't work - only $Component.theForm works.

Seems like a bad hack...I am not sure if i can rely on j_id2 being the same for ever - when I modified some code, it ended up being j_id1.

Is there  a way of accessing the table component without this hack?

In the code below, I am using Javascript to make sure only one row can be marked primary distributor. The code where I am trying to create the element id sets the checkbox that was selected at initially (selectChkBox) . deSelectOther allows only one to be selected from there on...

Code:
<apex:page controller="selectPreferredDistributorsController" tabStyle="Distributor__c" id="thePage">
<script>
var selectedChkbox;
var tableIdCount = 0;
function deSelectOthers(chkBox) {
if (chkBox.checked) {
if ((chkBox != selectedChkbox) && (selectedChkbox != null)) {
selectedChkbox.checked = false;
}
selectedChkbox = chkBox;
}
}
</script>
<apex:form id="theForm">
<apex:sectionHeader title="Select Preferred Distributors" />
<apex:pageBlock >
<apex:pageBlockButtons >
<apex:commandButton action="{!Save}" value="Save" />
<apex:commandButton action="{!Cancel}" value="Cancel" />
</apex:pageBlockButtons>
<!-- show the Table with selection -->
<apex:pageBlockTable value="{!PreferredDistributorWithSelects}" var="pd" id="theTable" frame="border">
<apex:column headerValue="Select">
<apex:inputCheckBox value="{!pd.selected}">
<apex:actionSupport event="onclick" rerender="primaryCheckBox" action="{!uncheckPrimaryIfChecked}"/>
</apex:inputCheckBox>
</apex:column>
<apex:column value="{!pd.pDistributor.name}" />
<apex:column headerValue="Primary Distributor">
<apex:inputCheckBox value="{!pd.primary}" id="primaryCheckBox" disabled="{!NOT(pd.selected)}" onclick="deSelectOthers(this)" />
<script>
if ("{!pd.primary}" == "true") {
var idForSelectedBox = "thePage:theForm:j_id2:theTable:"+tableIdCount+":primaryCheckBox";
selectedChkbox = document.getElementById(idForSelectedBox);
}
tableIdCount++;
</script>
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:form>
</apex:page>

 











Message Edited by GoForceGo on 12-22-2008 12:45 PM
This was selected as the best answer