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