Showing posts with label Visualforce. Show all posts
Showing posts with label Visualforce. Show all posts

Wednesday, January 25, 2012

Simple Method to Identify Blank Fields in Visualforce

I've often wondered why there is no ISBLANK() equivalent in Apex for developers to use when validating a Visualforce page. Maybe Salesforce always intended for developers to use the required attribute for the standard Visualforce components.

The problem with relying on field settings, Visualforce required attributes or object validation rules is that there is no consistent presentation of the error messages. For different sites, especially public ones, sometimes the validation errors need to be tailored specifically for the site.

To help this process along, I modularized the concept of ISBLANK() in the simple instance method below.
/**
 * Determine whether a given object is blank
 * or not, in the same manner as the ISBLANK()
 * Visualforce function.
 *
 * @param  o The object to examine
 * @return   Whether the object is blank
 */
public Boolean isBlank(Object o) {
    return o == null;
}   // public Boolean isBlank(Object o)

With this simple method (or variations thereof), developers can iterate through a List<Schema.SObjectField> of required fields and use controller.isBlank(Object) to validate the fields.
for (Schema.SObjectField field : requiredFields) {

    if (isBlank(application.get(field))) {
        addRequiredFieldError(field);
        isValid = false;
    }   // if (isBlank(application.get(field)))
    
}   // for each Schema.SObjectField in requiredFields

Saturday, November 5, 2011

JavaScript Hack to Render Red Bar for Required apex:selectList

I'm surprised that Salesforce doesn't support rendering apex:pageBlockSectionItem elements with the same red bar that identifies a required element.

All I wanted was for ...


... to look like ...


So, for consistency of developing a Visualforce page that has a consistent look-and-feel, I came up with a script snippet that seems to work to add the red bar to any apex:pageBlockSectionItem with the dataStyleClass set to "requiredData".

Here's the code:
/**
 * Add the Salesforce "required" appearance to an 
 * input cell.
 *
 * @param td The input cell to give a "required" 
 *           appearance.
 */
function addRequiredAppearance(td) {
    
    // Construct the wrapper div that goes inside 
    // the td.
    
    var requiredInputDiv = 
            document.createElement("div");

    requiredInputDiv.setAttribute(
            "class", "requiredInput");
    
    // Construct and append the div that renders 
    // as the red bar.
    
    var requiredBlockDiv = 
            document.createElement("div");

    requiredBlockDiv.setAttribute(
            "class", "requiredBlock");

    requiredInputDiv.appendChild(
            requiredBlockDiv);
    
    // Move each child from the td inside the 
    // wrapper div.
    
    while (td.firstChild) {
        requiredInputDiv.appendChild(
                td.removeChild(td.firstChild));
    }   // while (td.firstChild)
    
    // Append the wrapper div inside the td.
    
    td.appendChild(requiredInputDiv);
}   // function addRequiredAppearance(td)

// Iterate through every marked element to add 
// the required appearance.

jQuery(".requiredData").each(function (i) {
    addRequiredAppearance(this);
});

With the desired apex:pageBlockSectionItem elements marked, this code adds the red bar to compliment a Visualforce input element that has the required attribute set to "true".

As mentioned in the discussion thread, Salesforce may at any time change the way they render required fields. Since this was only tested in Winter '12, the hack may not work in a later release if applied as-is. However, the concept should still hold true, and as long as the function is modified appropriately all should be well.

Note: This hack uses the jQuery library for selecting marked elements.

Tuesday, July 19, 2011

Navigation Extension for Salesforce Sites

Thinking about how to best handle navigation within Salesforce Sites, I decided to try my luck with setting up an easily re-usable extension that can be applied to an entire site.

Fortunately, it looks like the apex:commandLink element paired with apex:param is able to produce the customizable navigation code I wanted.

Instead of writing out methods like the following...

public PageReference goToPage1() { ... }
public PageReference goToPage2() { ... }
public PageReference goToPage3() { ... }

all that's needed is...

public PageReference goToDestinationPage() { ... }

Much better, right? See the demo source code for more details.

Monday, July 18, 2011

Preview as Admin Feature for Salesforce Sites

I just discovered (1 year too late) the Preview as Admin feature for developing Salesforce Sites.  This has got to be one of the more useful things that I wish I had picked up earlier.

Basically, when developing a site and testing it as an anonymous user, error messages are sometimes hidden behind the annoying "Authorization Required" message.

Preview as Admin saves the day by delivering the anonymous user experience, enhanced with a useful description of the errors at the bottom of the page whenever errors appear.

Awesome, indeed.  I wonder what other features are out there that I don't know but should know...

Tuesday, April 26, 2011

Bug (?) with Rerendering an apex:pageBlockSection When apex:inputFile Exists Elsewhere on Page

I can understand throwing an error if we rerender part of a page that contains an apex:inputFile component, but I'm a bit confused about why rerendering part of a page that has no apex:inputFile component should generate the following error:
apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute.

I've created a demo for getting the error message, although you can't see the exact error message unless you deploy the page in your own sandbox. The source code can be downloaded here.

What I expected was that my partial page refresh of "Dependent Section" would work just fine. However, when I change the value in my controlling picklist, the page just fails, with the error mentioned above.

Is this a bug? I'm not sure what Salesforce would say, but it certainly is puzzling and extremely frustrating to me.

Monday, March 21, 2011

Bug (?) with apex:actionSupport Containing apex:param

I'm encountering some unexpected behavior with apex:actionSupport and apex:param in what I thought would be a simple application.

I've created a demo of an apex:select with an apex:actionSupport component to illustrate the problem.  The source code for the demo is also available.

What I expected to happen:

  1. Change the picklist value.
  2. Observe that assignToColor matches picklistColor.
  3. Observe that displayColor matches picklistColor.

What actually happens is that nothing changes except picklistColor.

Is this a bug or is this expected behavior?  I thought that apex:param could be used with apex:actionSupport components.

Sunday, March 20, 2011

Bug with Custom Component Rerender inside apex:repeat

I seem to have run into a bug with custom components trying to rerender parts of themselves: The rerendered component is able to display updated values as text, but formula evaluations of any updated values ignore the updated values and instead use initial values.

For example: Let's say I have a component has an apex:inputText with value="{!accountName}" that rerenders the parent apex:outputPanel during an onchange event. After rerendering, a few odd things happen:

  • The apex:inputText reverts to blank control.
  • If I display {!accountName} as text, the new value appears fine.
  • Any other components that have rendered="{!NOT(ISNULL(accountName))}" are still not rendered.

An experience is worth a million words, so I've put up a demo in my sandbox.  The source code for the demo is also available.

  1. In the Lead (standalone) section, enter something into the Name field.
  2. Click somewhere else on the page.  An Ajax refresh will occur, and the Text Insertions and Output Component Renders section should correctly update to reflect the new Name value.
  3. Now, in any Lead (from List) section, enter something into the Name field.
  4. Click somewhere else on the page.  An Ajax refresh will occur.
  5. Observe two problems:  The Name field is blanked out, and the Output Component Renders section did not change.

Hopefully, Salesforce Premier Support will tell me that the problem will be fixed immediately within the next 5 business days.  Hopefully...

Comparison of null to Integer Always Returning true

I ran across an interesting phenomenon when comparing null values to Integer values on Visualforce pages: The comparison always returns true!

  1. null < 0 evaluates to true
  2. null = 0 evaluates to true
  3. null > 0 evaluates to true

It appears that it's best not to compare null values to Integers, since the result is effectively useless.

ISBLANK(String) Returning false When String.length() Returns 0

I discovered this with version 21.0 of the Salesforce API: When a String's length is 0, the ISBLANK(String) Visualforce function will actually return false instead of true.

Visualforce Developer's Guide, Version 21.0 describes the ISBLANK function as follows:

Determines if an expression has a value and returns TRUE if it does not. If it contains a value, this function returns FALSE.

A search for ISNULL in the documentation returned no real results, which makes me wonder if ISNULL has been unofficially deprecated in favor of ISBLANK.

So, it appears that the only way to know for sure whether a String input has been blanked out is to use the length method and compare it to 0.

Saturday, March 19, 2011

apex:define Tag Precedence in Visualforce Templates

I learned something interesting through trial-and-error yesterday:

If you define a set of templates where each successive template "extends" the previous template, the apex:insert tag in all previous templates are available for definition in the final implementing page.

For example, let's take take the following hierarchy of templates:
  1. SiteMasterTemplate
  2. SiteDepartmentTemplate--contains an apex:composition with template="SiteMasterTemplate"
  3. SiteProductTemplate--contains an apex:composition with template="SiteDepartmentTemplate"

Now, if we create a page called SiteSuperMotor that contains an apex:composition with template="SiteProductTemplate", our page can actually use apex:define that define apex:insert elements in SiteMasterTemplate!

Furthermore, let's say that we have apex:insert elements in both SiteDepartmentTemplate or SiteProductTemplate that share the same name attribute as an apex:insert in SiteMasterTemplate, then the SiteSuperMotor page's apex:define would only define the apex:insert in SiteMasterTemplate!

Friday, March 18, 2011

Visualforce Misinterprets Empty xmlns Attributes

For unknown reasons, Visualforce does not handle an empty xmlns attribute inside a DIV tag. Unexpected behavior will result if a DIV tag is used inside a Visualforce page with xmlns="" specified as an attribute.

If a Visualforce page that contains simple HTML is not rendering like the page from which the HTML was copied, checking for empty xmlns attributes may be the key to fixing the problem.

Tuesday, March 8, 2011

jQuery in Custom Input Components

I'm trying to write my own custom input component that updates values based on button clicks. The component idea is similar to the enhanced jQuery input components created by Joel Dietz, although far less universal in application. An example of Joel's components can be found below.

"enhancedText.component"
sfdcjqueryenhancements

"EnhancedTextController.cls"
sfdcjqueryenhancements

One trick to making the component inputs work is to factor in the quirky DOM ID's generated by Salesforce. This is addressed in the following blog post from Wes Nolte.

"VisualForce Element Ids in jQuery selectors"
The Silver Lining

To clarify Wes's post, the function is defined as follows:

function esc(myid) {
    return '#' + myid.replace(/(:|\.)/g,'\\\\$1');
}

Also, the esc function cannot be called with a literal! The String must be stored in a var first, and the var should then be passed to the esc function.

Whew! I'm not done with my custom input component yet, but I definitely see bright rays of hope.

Monday, March 7, 2011

How to Write to a Custom Component Attribute

I spent a long time trying to figure out why the following code was not working:

<apex:component id="this" controller="MySiteInputAccountIdCtrl"
selfClosing="true">

<apex:attribute name="value" type="Id"
description="Account Id to pass back to the page."
assignTo="{!accountId}"
required="true"/>

<apex:inputText id="accountIdIText"
value="{!accountId}"/>

</apex:component>

accountId was a simple property with a vanilla pair of getter and setter methods. When the component is rendered as a component like <c:MySiteInputAccountId value="{!contact.AccountId}>, contact.AccountId would not update no matter what I typed into the input field.

Several hours later, I took a look back at a custom component that worked (and was also created by me after a similar bout of confusion)... And it turns out that I was writing the input to the wrong object. The correct component markup is as follows:

<apex:component id="this" controller="MySiteInputAccountIdCtrl"
selfClosing="true">

<apex:attribute name="value" type="Id"
description="Account Id to pass back to the page."
assignTo="{!accountId}"
required="true"/>

<apex:inputText id="accountIdIText"
value="{!value}"/>

</apex:component>

I can't believe I forgot this resolution to a 3-hour frustration almost immediately... just to experience a new 6-hour frustration on the same topic. Hopefully writing this down will help me remember my lesson and avoid a third incident.

Friday, February 25, 2011

Conversion Error setting value 'value1 value2 value3' for '#{myMultiselectPicklistValue}'.

Salesforce is great. Apex is great. Visualforce is great.

Until you run into bizarre errors with no obvious explanation, such as the following error:
Conversion Error setting value 'value1 value2 value3' for '#{myMultiselectPicklistValue}'.

This error comes from trying to save the selections from an apex:selectCheckboxes component into a multi-select picklist field. You would think that it's as easy as simply specifying {!property} for the value attribute of the apex:selectCheckboxes component. But, no, it's not.

Multi-select picklist values are stored as String values, with each option delimited with a semicolon.  This is great to know, but what happens with apex:selectCheckboxes? apex:selectCheckboxes expects a List<String>! This discussion board post hints at the annoyance awaiting developers: "Checkboxes not saving".

Basically, to spell it out for myself and for others, here's what we have to do as developers working around this problem.

What we want to write is:

<apex:selectcheckboxes value="{!mPicklistValue}">
... and in the controller ...

public String mPicklistValue { get; set; }

Instead, what we have to write is:

<apex:selectcheckboxes value="{!mPicklistValues}">
... and in the overblown controller ...
private String mPicklistValue;
public List<String> getMPicklistValues() {
    List<String> values = null;
    // Convert mPicklistValue into List of 
    // String values

    if (mPicklistValue != null)
        values = mPicklistValue.split(';');

    return values;
}   // List<String> getMPicklistValues()
public void setMPicklistValues(
        List<String> values) {

    // Convert values into a semilcolon-delimited
    // String value

    if (values == null) {
        mPicklistValue = null;
    }
    else {
        mPicklistValue = '';
        
        for (String value : values) {
            mPicklistValue += value + ';';
        }
    }
}   // void setMPicklistValues(List<String>)

Note the plural name of the custom getter and setter methods. Cheers, indeed.

Tuesday, January 18, 2011

Comparing Salesforce Record ID's in Visualforce

Strangely enough, the Visualforce comparison operator does not work when comparing a 15-digit record ID with an 18-digit one. The workaround I concocted is to use a very specific Boolean property in my controller extension to perform the comparison instead, since I really wanted to avoid using any kind of custom ID conversion code that may become invalid in future releases.

public Boolean isCitizenshipCountryUnitedStates {
get {
return stdCtrler.getRecord().get('Citizenship_Country__c') == unitedStatesCountryId;
}
}

The situation I have is that I want to compare the Country of Citizenship specified on an application record (for custom object Application) to see whether the applicant is a U.S. citizen. Country of Citizenship is a Lookup(Country) field, with Country also being a custom object.

Without hardcoding the Id for the United States country, I defined a unitedStatesCountryId property as follows:

 public Id unitedStatesCountryId {
get {
if (unitedStatesCountryId == null) {
List matchingCountries =
[SELECT Id FROM Country__c
WHERE Name = 'United States'];

if (matchingCountries.size() > 0) {
unitedStatesCountryId = matchingCountries.get(0).Id;
}
}

return unitedStatesCountryId;
}

set;
}

When I evaluate the expression {!Application__c.Citizenship_Country__c = unitedStatesCountryId} in my Visualforce page, the expression always returns false. When digging a little deeper, I found that Application__c.Citizenship_Country__c was returning 'a0DT0000006NIDM' while unitedStatesCountryId was returning 'a0DT0000006NIDMMA4'! Visualforce appeared to be comparing the two values as Strings and not as Ids.

Finding no Visualforce-native solution, I had to go the route of creating the isCitizenshipCountryUnitedStates property in my controller extension. Needless to say, this quirk in Salesforce does not make me happy.

Tuesday, August 24, 2010

Passing Data between Visualforce Pages with Controllers and Extensions

I spent a fair amount of time yesterday trying to figure out how to pass information back and forth between Visualforce pages, despite finding a pretty good article in the Force.com IDE Library.

"Creating a Wizard with Visualforce Pages"
http://www.salesforce.com/us/developer/docs/cookbook/Content/vf_wizard.htm

The article got me started, but then when I moved to the actual implementation in which I wanted to use controller extensions, I would be able to pass data between pages until I started including the extensions. I was left scratching away at my head for a while.

Finally, after many trials and errors, I came to the following conclusion: In order for data to be maintained from page to page, both the controller and the extension(s) referenced must be the same across all pages in the group.

For example, assume that the following Apex classes exist:
  • MyController
  • MyPage1Ext
  • MyPage2Ext

Also assume that I am trying to pass data between the following two pages, each of which uses the functionality included in the corresponding extension:
  • MyPage1
  • MyPage2

If I specify different attributes extensions="MyPage1Ext" and extensions="MyPage2Ext" for the two pages, then I am unable to pass data back and forth even if I specify the same controller. The trick is to use the same controller and the same extensions on both pages via the attribute extensions="MyPage1Ext,MyPage2Ext".

This is a bit depressing to know, but at least now I have a framework within I can build my actual application.

Tuesday, July 27, 2010

Salesforce, Internet Explorer 8 and the Missing DOCTYPE Declaration

All of my frustration with IE8 is now divided 25-75 between IE8 and Salesforce, with most of the frustration on Salesforce.

The layout scheme I described in my previous post works in all browsers except in IE8 when implemented as a Salesforce Visualforce page. Offline, I was able to reproduce the issue with the source code saved from the offending Visualforce page. Then, furthermore I was able to fix the issue by adding a simple DOCTYPE declaration at the top of the page.

The layout issues appear to be caused by the fact that Salesforce does not generate a !DOCTYPE tag at the top of the page, and IE8 just happens to assume a different !DOCTYPE than Firefox or Chrome. The !DOCTYPE that IE8 assumes must not be compatible with CSS 2.1 and HTML 4.1, the standards that I am trying to follow (although I'm probably making some mistakes along the way as well).

Regardless, Salesforce's Developerforce article "Using the Salesforce CSS in Your Apps" explicitly states (at my last viewing of the page on July 27, 2010 at 6:13 PM EDT):

Please ensure that you define the following DOCTYPE at the top of your HTML:

<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">


Ironically, if I try to add the !DOCTYPE tag before the apex:page tag, I get the following error:

Error: java.lang.NullPointerException
Error: null


Furthermore, if I try to add the !DOCTYPE tag immediately after the apex:page tag, I get a different error:

Error: A DOCTYPE is not allowed in content. at line 2


I found a Salesforce Community thread that talked about this issue, but the resolution is ambiguous to me and does not clearly show how to get the infinitely important !DOCTYPE tag into the page. The thread "Changing doctype of a Visualforce Page" shows many people having success with getting the !DOCTYPE tag to stick, but for some reason it is still not working for me.

I'm going to log a case with Premier Support and poke around some more to see if I can't get this to work in the next 15 minutes before the lights shut off here in the office.

Wednesday, July 21, 2010

apex:actionSupport Does Not Work in Form with Required apex:input Components

After spending over two hours troubleshooting what seemed like a ridiculous problem, I discovered that the apex:actionSupport component does not work if any other apex:input* components have the required attribute set to true.

Unbelievable, the kind of stuff that's missing from the Visualforce documentation...

Monday, July 19, 2010

Tips on Getting Started with Ajax in Salesforce

It appears that there are a few things to keep in mind in order to reduce frustration and wasted time when implementing AJAX behavior with Salesforce.

  • The reRender attribute only works with apex:output* standard components.

  • The apex:outputPanel standard component produces a DIV tag, which means that it cannot be used to encapsulate table rows or table cells. However, the component can be used within a table cell.



I don't know why, but it took me a few hours to figure this out while I was trying to achieve Ajax behavior with as few Apex components as possible.