Friday 30 July 2010

"Check All" checkbox in an apex:dataTable (without using scripts)

Recently, a business requirement emerged for a client whereby a user needed to be able to select a number of contacts from a data table, and then carry out some business logic on those selected clients. The process of selecting several contacts using a wrapper class is easy enough, but what about a simple way to select and deselect all of the records in one go? This can be achieved placing a checkbox in the header element of the column. I found an example solution that uses a javascript function embedded on the page to copy the true/false value of the header checkbox to all the boxes in the column.

This certainly does the job, but I found from a design perspective it had some limitations. The javascript function it calls gets the elements by tag, specifically the input tag, which may be used elsewhere if the page you are designing contains more than just this data table. Also, placing selection controls in scripts divides the selection logic between two entities; the page and the apex controller class.

With this in mind, I started to think about how I could develop an equivalent solution that uses logic inside the controller to change the state of all of the checkboxes. This is the solution I came up with:

Controller:
public class CheckboxCheckAllController {
 
 public List<ContactWrapper> allContacts { get; set; }
 public Boolean allChecked { get; set; }
 
 public CheckboxCheckAllController () {
  allContacts = new List<ContactWrapper>();
  allChecked = false;
  
  for(Contact contact: [select Name, Title, Department, Email from Contact ]){ 
   allContacts.add(new ContactWrapper(contact));
  } 
 }
 
 public PageReference CheckAll(){
  
  for(ContactWrapper contact : allContacts){
   contact.selected = allChecked;
  }
  
  return null;
 }
 
 public PageReference ProcessSelectedContacts(){
  
  for (ContactWrapper contactWrapper : allContacts ){
   if(contactWrapper.selected == true){
    /* Do Some Processing */
   }
  }
  
  return null;
 }

 public class ContactWrapper {
  
  public Contact con{get; set;}
        public Boolean selected {get; set;}
        
        public ContactWrapper(Contact c){
            con = c;
            selected = false;
        }
        
 }
}

Page:
<apex:page controller="CheckboxCheckAllController">

  <apex:form>

    <apex:dataTable value="{!allContacts}" var="c" id="contactsTable">
      <apex:column>
        <apex:facet name="header">
          <apex:inputCheckbox value="{!allChecked}">
            <apex:actionSupport event="onclick" action="{!CheckAll}"
                                rerender="contactsTable"/>
          </apex:inputCheckbox>
        </apex:facet>
        <apex:inputCheckbox value="{!c.selected}"/>
      </apex:column>
      <apex:column value="{!c.con.Name}" headervalue="Full Name"/>
      <apex:column value="{!c.con.Title}" headervalue="Title"/>
      <apex:column value="{!c.con.Department}" 
                   headervalue="Department"/>
      <apex:column value="{!c.con.Email}" headervalue="Email"/>
    </apex:dataTable>

    <apex:commandButton action="{!ProcessSelectedContacts}" 
                        value="Process Selected Contacts"/>

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

Page In Browser:


Admittedly, it provides a slightly slower response time than the pure javascript, but I think this provides benefits in singularity of location of control code and adaptability, as you can control the behaviour of the selection using array logic.

Thursday 22 July 2010

Report Builder Developer Preview Webinar Feedback

Today I took part in the new Report Builder Webinar, which included a demonstration of the new builder tool Salesforce has created in response to users commenting that the existing wizard was clumsy and made making changes a very arduous process. In the past, creating a single report involved plodding through seven setup screens, which all have to be repeated if you want to make a single change, and there is no clue as to what the report will look like until all these preparation steps have been completed. The new tool is much more interactive, providing a single screen with an active preview of the report based on a small portion of your existing data. The report is refreshed every time you make a change, so you always have an exact idea of the reports end appearance.

The Report Builder is already available in all developer editions. To really appreciate the improvement, I had a go at creating a few simple reports using both the standard wizard and the new report builder. Using the new tool is a vast improvement and a lot more intuitive. I was able to add fields, change the order of fields, order data, group rows and impose date period restrictions on fields immediately using built in menus. The report builder can create reports for both standard and custom objects, and even pre-prepares standard report templates for custom objects based on their relationships with other Salesforce objects.

To access the report builder, click on the Reports tab (can be included in an app through setup if not there already), and then on the “Create Report With Report Builder” button. The “Create New Custom Report” button allows you to use the old report wizard.

The report builder will be rolled out to all editions in the Winter ’11 release later this year, so is not immediately available to create and manipulate reports in Salesforce applications. However, because the underlying metadata for reports is the same no matter which composer is used, you could potentially use developer accounts to create the reports using the new builder and then copy the metadata to your production application. For this to work you must ensure that the underlying object models between the developer copy and your production org are the same.

Tuesday 20 July 2010

Frustrations with the home page default view for objects

Throughout my time using the Force platform, I have found the default view displayed on object home pages clumsy and counter intuitive. Here is an example of a home page for a custom object, merchandise, that featured as part of the exercises inside the Force.com workbook.


Now, imagine you are looking at this view for the first time, you see a list of Merchandise objects (Wee Speedboat, Wee Train, Wee Jet) and the view indicator says 'All', this must be all of the Merchandise records in the system right? Wrong!! this is actually a list of merchandise records that were recently viewed by you. This is indicated by a small drop down box on the right hand side of the screen, where you don't instinctively think to look. Worse still, the two other options in the list 'Recently Created' and 'Recently Modified' only show the records that you personally have created or modified, so from an overall data perspective, you have no idea what is going on.

While these views are useful in certain approaches and data models, I do not understand why they are part of a default view that cannot be changed. If you open the 'All' view by clicking on the 'Go!' button, you get the following view on the screen.


I feel that even this would be a more suitable home page, with 'Recently Modified', 'Recently Viewed' and 'Recently Created' all as standard views on the drop down menu (with the words 'by you' after them for clarity). If a report page block was required as is necessary with some objects, this could be included underneath the view section.

The issue has been raised in the Salesforce idea exchange but no solution has been introduced in the four years since this was posted, which I find frankly unbelievable. Salesforce's view on this issue seems to be that if customizations to the default view are required, then a Visualforce page can be created with the appropriate appearance. Still, I don't see why the appearance can't be changed in the same way that objects have an individual record page layout that can be customised. It could be as simple as allowing the user to decide if they want the recent list and the default view that they wish to use.

With this in mind, I am seriously considering trying my hand at developing an application for the Salesforce AppExchange which would provide a simple menu that administrators could click through to create a basic custom home page for a particular object, views included.

Thursday 1 July 2010

Adding alphabetical lists in Salesforce PDFs

One of the better features of Salesforce and apex programming is the ability to generate Visualforce pages as PDFs by simply using the renderAs="pdf" attribute notation in the apex:page tag. However, recently when using this feature to generate a list of conditions, I noticed that whenever you define an apex datalist, or an ordered list in html that uses letters (a,b,c,d) as the markers for each element in the list, these are rendered in the PDF incorrectly.

Take the following example of a page and controller:

Page:
<apex:page showHeader="false" controller="LetterListController" standardStylesheets="false" sidebar="false">

<apex:dataList value="{!accounts}" type="a" var="account" id="theList" >
<apex:outputText value="{!account.name}"/>
</apex:dataList>

<ol type="a">
<li>First Item</li>
<li>Second Item</li>
<li>Third Item</li>
<li>Last Item</li>
</ol>

</apex:page>

Controller:
public class LetterListController {

List<Account> accounts;

public List<Account> getAccounts() {
if(accounts == null) accounts = [select name from account limit 10];
return accounts;
}
}


When the Letter List page is rendered in Salesforce, the following output appears-


Absolutely fine, but when we add the renderAs="pdf" attribute in the page tag definition, the PDF output is -


The apex datalist has been rendered as a list with blobs, while the html list has been changed to be ordered by numbers. This is quite frustrating, but the solution is simple. You can choose the bullet point class of a list using the css attribute list-style-type. So all we need to do is create a style class on the page inside head tags, and then apply this class to the two lists.

Page:

<apex:page showHeader="false" controller="LetterListController" standardStylesheets="false" sidebar="false" renderas="PDF">

<head>
<style>
.letterList{
list-style-type: lower-alpha;
}
</style>
</head>


<apex:dataList value="{!accounts}" var="account" id="theList" styleClass="letterList">
<apex:outputText value="{!account.name}"/>
</apex:dataList>

<ol class="letterList" type="a">
<li>First Item</li>
<li>Second Item</li>
<li>Third Item</li>
<li>Last Item</li>
</ol>

</apex:page>

The output PDF is now -