20

I've reduced my issue to the following example:

<apex:page >
  <apex:variable var="blank" value="" />
  <p> Contains(blank, 'foo'): <apex:outputText value="'TRUE'" rendered="{!CONTAINS(blank, 'foo')}" /></p>
  <p> Not(Contains(blank, 'foo')): <apex:outputText value="'TRUE'" rendered="{!NOT(CONTAINS(blank, 'foo'))}" /></p>
  <p> NULLVALUE(blank, 'x'): <apex:outputText value="{! NULLVALUE(blank,'XXXXX')}" /></p>
  <p> BLANKVALUE(blank, 'x'): <apex:outputText value="{! BLANKVALUE(blank,'XXXXX')}" /></p>
  <p> ISBLANK(blank, 'x'): <apex:outputText value="{! ISBLANK(blank)}" /></p>
</apex:page>

Which produces:

Contains(blank, 'foo'):'TRUE'
Not(Contains(blank, 'foo')):'TRUE'
NULLVALUE(blank, 'x'):
BLANKVALUE(blank, 'x'):
ISBLANK(blank, 'x'):false

How can Contains(blank, 'foo') and Not(Contains(blank, 'foo')) both return true? Why doesn't BLANKVALUE or ISBLANK recognize the var blank as being blank?

I took this a step further, and created this controller:

public class formulaTestController {
  public string getNullValField() { return null;}
  public string getBlankValField() {return ''; }
}

If I add this controller to the page and change the variable to <apex:variable var="blank" value="{!blankValField}" />, I get the same results as above. If I change it to <apex:variable var="blank" value="{!nullValField}" />, I get this:

Contains(blank, 'foo'):'TRUE'
Not(Contains(blank, 'foo')):'TRUE'
NULLVALUE(blank, 'x'):XXXXX
BLANKVALUE(blank, 'x'):XXXXX
ISBLANK(blank, 'x'):true 

So it appears that BLANKVALUE and ISBLANK really operate on nulls, but not on blanks. Is this expected? Especially w/r/t BLANKVALUE, as NULLVALUE also exists. And why do the Contains() and (Not(Contains()) tests still produce the same result?!

In the end, I need to reliably handle a controller value that may be blank or null, treating these as special cases in my VF logic. How can I do so?

EDIT: Per suggested answer, I tried wrapping Contains test in IF(). Note that I moved the formula into outputText's Value to make the result explicit:

<apex:page >
  <apex:variable var="blank" value="" />
  <p> IF(CONTAINS(blank, 'foo'),true, false): <apex:outputText value="{!IF(CONTAINS(blank, 'foo'),true, false)}" /></p>
  <p> IF(NOT(CONTAINS(blank, 'foo')),true, false): <apex:outputText value="{!IF(NOT(CONTAINS(blank, 'foo')),true, false)}" /></p>
  <p> IF(CONTAINS(blank, 'foo'),'true', 'false'): <apex:outputText value="{!IF(CONTAINS(blank, 'foo'),'true', 'false')}" /></p>
  <p> IF(NOT(CONTAINS(blank, 'foo')),'true', 'false'): <apex:outputText value="{!IF(NOT(CONTAINS(blank, 'foo')),'true', 'false')}" /></p>
</apex:page>

No change:

IF(CONTAINS(blank, 'foo'),true, false):false
IF(NOT(CONTAINS(blank, 'foo')),true, false):false
IF(CONTAINS(blank, 'foo'),'true', 'false'):false
IF(NOT(CONTAINS(blank, 'foo')),'true', 'false'):false

UPDATE: As noted by @tlfu below, Salesforce is now tracking this issue. You may recognize the issue description :)

4 Answers 4

15

I observed that ISBLANK(null)=true, ISBLANK('')=false regardless of whether bound to an SObject text field or a public String variable in the controller. Compare this to how the Apex String.isBlank() method works:

String.isBlank(null) = true;
String.isBlank('') = true;

This undocumented inconsistency between Visualforce vs. Apex behavior resulted in a tricky bug in one of my Visualforce pages where I had replaced this:

rendered="{!ISNULL(stringVar) || stringVar==''}" 

with this, because I was under the impression they were functionally equivalent

rendered="{!ISBLANK(stringVar)}"

I've opened a case via our partner portal account to have Salesforce weigh in on this. I'll let you know if anything useful results.

1
  • I received the following response to my case about the inconsistent (wrong, IMHO) behavior of ISBLANK: I was able to reproduce this issue and this is a known issue which our R&D team is aware of. They are working on it but the fix may not be released immediately. I will let you know when the fix is released. You can keep track of the status of this bug , once it is posted on Known issues site.
    – tlfu
    May 31, 2013 at 9:33
5

It seems that the variables declared with blank values do not really contain zero-length strings as intended.

Consider the following Visualforce page:

<apex:page>
    <apex:variable var="blank" value="" />
    <p><apex:outputText value="NOT TRIMMED: {!ISBLANK(blank)}" /></p>
    <p><apex:outputText value="TRIMMED: {!ISBLANK(TRIM(blank))}" /></p>
</apex:page>

The output of this is:

NOT TRIMMED: false
TRIMMED: true

Since using the TRIM() function inside of the ISBLANK() function returned the correct result, perhaps it's the use of variables representing zero-length strings that is broken, not the ISBLANK() function.

3

When I changed the page to be this i.e. 1) change var to be a blank field on Account and 2) AND the output of CONTAINS with TRUE, it seems to work as expected:

  <apex:page standardController="Account">
  <apex:variable var="blank" value="{!Account.Fax}" />
  <p> Contains(blank, 'foo'): <apex:outputText value="'TRUE'" rendered="{! TRUE && CONTAINS(blank, 'foo')}" /></p>
  <p> Not(Contains(blank, 'foo')): <apex:outputText value="'TRUE'" rendered="{! TRUE && NOT(CONTAINS(blank, 'foo'))}" /></p>
  <p> NULLVALUE(blank, 'x'): <apex:outputText value="{! NULLVALUE(blank,'XXXXX')}" /></p>
  <p> BLANKVALUE(blank, 'x'): <apex:outputText value="{! BLANKVALUE(blank,'XXXXX')}" /></p>
  <p> ISBLANK(blank, 'x'): <apex:outputText value="{! ISBLANK(blank)}" /></p>
</apex:page>

I have a feeling that the functions are attuned to working with sObject fields rather than a dynamic variable (or even if a variable, at least initialised to an sObject field) in Visualforce.

Contains(blank, 'foo'):

Not(Contains(blank, 'foo')):

NULLVALUE(blank, 'x'):XXXXX

BLANKVALUE(blank, 'x'):XXXXX

ISBLANK(blank, 'x'):true

So its almost as if the output of CONTAINS is somehow messing up, although it seems to be alright after you AND it with TRUE, which in essence is just transparent and lets the actual value of it pass through.

1
  • On the use of TRUE && Contains(...): I don't see how this changes anything; in your example both Contains() and Not(Contains()) tests still return the same result. I tried adding it to a number of my test variations; it doesn't appear to change the result. On the use of Account.fax: a quick anonymous Apex test proves that when Account.fax is "blank", it is null. So I still believe that ISBLANK and ISNULL are identical, and only operate on Nulls, not blanks (which to my mind should include empty string). Nov 19, 2012 at 14:14
1

This is actually an issue with your use of <apex:variable>. According to the documentation, the value parameter is supposed to refer to an object. Think of it more as an alias than anything else, to simplify your later references. For example, after <apex:variable var="r" value="{!Custom_Object__c.Related_Object__r}"/>, you can easily refer to fields r.foo, r.bar and r.baz.

So, in your example, the blank alias is failing, so trying to use it in other functions leads to inconsistent results.

You must log in to answer this question.

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