Monthly Archives: February 2014

Visualforce – jQuery pattern – enforce only single checkbox in a list

Over the last few months, I’ve dabbled in adding some client-side improvements to Visualforce pages. But, I’ve resisted doing much with Javascript and jQuery in order to keep the development models limited to just Visualforce, Apex, and Force.com. There was something about a blend of Javascript and VF markup on a page that I always found jarring and hard to read and understand – too much syntactic dissonance

But, jQuery and jQuery Mobile are highly recommended for developing Salesforce1 apps. After reading jQuery Mobile Web Development Essentials, Second Edition by Camden and Matthews, I discovered a nice organizing pattern for jQuery that was a good place to add in all of a page’s jQuery event handlers (see chapter 8). So, I decided to apply it to a commonly-occurring problem – display a list with checkboxes next to each row, but allow only one box to be checked before submission to the server. This avoids writing controller code to enforce a single selection (as radiobuttons can’t easily be applied to table rows).

So, here it is where the table displays a list of Quotes on an Opportunity and only one can be specified for the action

<apex:page standardController="Opportunity" extensions="OppoControllerExtension">

	<apex:includescript value="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" />
	<script>
		$j = jQuery.noConflict();		//	reassign $ to $j to avoid conflict with VF
		$j(function() {				// 	jQuery shorthand for $(document).ready(function() ..  Wait until page loads 
			//	define the application
			var	OppoToSvcContract	= {};
			
			(function(app) {		//	Define anon function
				//	--------------------------
				//	variable definitions
				//	--------------------------
		
				//	--------------------------
				//	init:	do initializations
				//	--------------------------
				app.init	= function() {
					app.bindings();		//	establish bindings	
				};
		
				//	-----------------------------------
				//	bindings:  events to event handlers
				//	-----------------------------------
				app.bindings = function() {
					// set up binding for onclick checkbox: Unchecks other checkboxes
					$j('input[id*="qCheckbox"]').click(function(e) {		// attach anonymous fn as event handler to click event for all DOM checkbox whose id attr contains qCheckbox, $j(this) available
						var clickedElmId	= $j(this).attr('id');			// DOM elm that was clicked
						$j('input[id*="qCheckbox"]').each(function() {		// Go thru all checkboxes
							if ($j(this).attr('id') != clickedElmId) {		// if id diff than clicked elm id, uncheck	
								$j(this).removeProp('checked');
							}
						});
					});													// end binding
				};														// end all bindings
				
				app.init();				// Anon function invokes its own init function
			}) (OppoToSvcContract);		// Execute anon function passing in an object representing our application
				
				
		});	
	</script>





	<apex:sectionHeader title="Create a Service Contract from this Opportunity's Quotes:" subTitle="{!Opportunity.name}"/>
	
	<apex:form id="theForm">
		<apex:pageMessages/> 
		<apex:pageBlock >
			<apex:pageBlockButtons location="top">
		 		<apex:commandbutton value="Create Service Contract from Oppo+Quote" 
						action="{!createSvcContract}"/>
		 		<apex:commandbutton value="Cancel" immediate="true" action="{!cancel}"/>      
			</apex:pageBlockButtons>
			
			
		</apex:pageBlock>
		
		<apex:pageBlock title="Quotes (to use as source for Service Contract line items)">
	
			<apex:pageBlockTable id="quotesTbl" value="{!availQuotes}" var="aq" style="width:90%; border-color: rgb(248,248,248);" headerClass="quoteTblHdr">
                <apex:column headerValue="Select One" style="width: 5%;">
                	<apex:inputCheckbox id="qCheckbox" value="{!aq.isSelected}"/>  <!--  jQuery event handler takes care of only one box checkable -->
                </apex:column>   
                <apex:column headerValue="Name" style="width: 10%">
					<apex:outputLink value="{!URLFOR($Action.Quote__c.View,aq.qw.q.id)}">{!aq.qw.q.name} {!aq.forecastMsg}</apex:outputLink> 
				</apex:column>    
			</apex:pageBlockTable>
         </apex:pageBlock>		
	</apex:form>
</apex:page>

Aligning labels with selectRadio within a pageBlockSection

Here was the issue:

I had a pageBlockSection with a mix of VF components bound to SObject fields and components bound to controller properties. In the latter case, I was using selectRadio.

The label on the selectRadio did not vertically align with the top radio button. You can see an aligned and misaligned example in the picture below.
Aligning label and selectRadio
Here is the VF markup for the aligned and misaligned example:
Solution: Use style="margin-top: -10px;".

<apex:pageBlockSection title="New Quote Wizard Parameters" columns="1" collapsible="false" id="quoteWizParams">
				
	<apex:selectRadio label="Type" value="{!quoteWiz.selectedQuoteWizType}" 
                          layout="pageDirection" id="quoteWizTypeRadios" 
                          style="margin-top: -10px;">
  		<apex:selectOptions value="{!quoteWiz.quoteWizTypeOptionList}"/>
        </apex:selectRadio>
				
	<apex:pageBlockSectionItem >
		<apex:outputText value="Service term (in months)"/>
		<apex:inputText value="{!quoteWiz.initServiceTermMos}"/>
	</apex:pageBlockSectionItem>
							
	<apex:selectRadio label="SnS Level" value="{!quoteWiz.selectedSnsLevel}" 
                          layout="pageDirection" id="snsLevelRadios">
	        <apex:selectOptions value="{!quoteWiz.snsLevelOptionList}"/>
	</apex:selectRadio>
</apex:pageBlockSection>

Side note: The use of the VF attribute label avoids the need for pageBlockSectionItem markup – SFDC automatically puts the label into the proper cell as if you were using apex:outputLabel.