Streamline your script tokens with a #displayIfFilled Velocimacro
Printing fallback content when a Lead field is blank is a basic Velocity task. You can do it in a few lines of clunky code… but that's a few lines too many!
Seeking a one-liner, you might reach for Velocity's built-in $display.alt
. But that won't fill the bill in Marketo-land. You see, $display.alt($field, $fallback)
outputs the fallback if the field is null
. But null
isn't the same as the empty String that Marketo uses for unfilled Lead fields.
Therefore, the code
Dear ${display.alt($lead.FirstName,"Friend")},
will only ever output
Dear Joe,
or
Dear ,
It will never fall back to Dear Friend,
because Marketo ensures $lead.FirstName
is always a String of some kind, never null
.[1]
So without any other tools at your disposal, you're left with a typically wordy #if
block:
Dear ##
#if( $lead.FirstName.isEmpty() )
Friend,##
#else
$lead.FirstName,##
#end
This will work fine, but as your scripts get cluttered with repeats of this same structure, you start to go a little crazy.[2]
The good news is there's a short Velocimacro you can include globally that greatly lightens the load. Once you set up the #displayIfFilled
macro, you can reduce the logic to one easy-to-read line:
Dear #displayIfFilled($lead.FirstName, "Friend"),
Building #displayIfFilled
Here's the macro definition:
#macro ( displayIfFilled $checkValue $fallbackValue )
#if( !($checkValue.isEmpty()) && !($checkValue == $display.get("0")) )
$!checkValue##
#else
$!fallbackValue##
#end
#end
As you can see, #displayIfFilled
takes 2 self-explanatory arguments: the field to check for filled-ness, and the fallback value.
#displayIfFilled
is designed to treat null
the same as the empty String. It thus covers a superset of the cases covered by$display.alt
, so you may never be tempted by the latter function again.
Step further out: #displayIf
We can abstract the functionality of #displayIfFilled
into a more general #displayIf
macro:
#macro ( displayIf $truePredicate $trueValue $falseValue )
#if( $truePredicate )
$!trueValue##
#else
$!falseValue##
#end
#end
#displayIf
takes 3 arguments: a Boolean, the value to output if the Boolean is true
, and the output if the Boolean is false
.
To emulate #displayIfFilled
, pass an isEmpty()
check as the first arg (the $truePredicate
):
Dear #displayIf($lead.FirstName.isEmpty(), "Friend", $lead.FirstName),
Lots of tricks up your sleeve with #displayIf
. Say you want to switch output based on a specific non-empty value…
Your #displayIf($lead.trialType.equals("Other"), "VIP", ${lead.trialType}) trial is almost over!
… or output based on a date/time property…
Good #displayIf($calNow.get($calFields.AM_PM).equals($calFields.AM), "mornin'", "aft'noon")!
… or anything that can be expressed as if-then-else!
Of course, #displayIf
can be overused; past a certain point of complexity, you should be using #if
-#else
on separate lines. But where a one-liner doesn't hurt readability, I say use it. #displayIf
is critical to my sanity (if I have any left) as an avid Velocity coder.
Stay functional
There's another detail that you'd eventually learn on your own, but I'll spoil it to save you time.
When passing macro arguments in parentheses, you can include any chain of function calls but not syntactical expressions. For example, though Boolean operators and expressions are valid in other parts of Velocity, you can't do:
#displayIf(!$lead.FirstName.isEmpty(), "${lead.FirstName}'s", "Your") special offer is ready!
That won't compile because of the !
. Velocity's parser doesn't accept operators in that place (nor would it accept <
, >
or ==
operators there).
Instead, either chain with the equals()
function:
#displayIf($lead.FirstName.isEmpty().equals(false), "${lead.FirstName}'s", "Your") special offer is ready!
or — as some programming style guides suggest anyway — don't rely on negated Booleans and instead put your true
case first:
#displayIf($lead.FirstName.isEmpty(), "Your", "${lead.FirstName}'s") special offer is ready!
To be clear, this doesn't mean you can't use all manner of operators and expressions to construct Boolean values, you just can't use them directly inside the parentheses when calling a macro. This will work fine:
#set( $hasCompany = !$lead.Company.isEmpty() && !$lead.Company.equals("N/A") )
Is #displayIf($hasCompany, $lead.Company, "your family") in the mood for pizza?
Disrupts the dream of a one-line solution, though.
What's with $display.get("0")
?
Ah, yes. I don't want to overwhelm you earlier with the details of null
checking in Velocity.
$display.get("0")
(up above in the first #displayIfFilled
macro) gets the value of a reference that is guaranteed to not exist, i.e. guaranteed to be null
, in any Velocity context.
Why is it guaranteed to not exist? Because neither Java nor Velocity variable names are allowed to begin with a number.
Why not compare to the literal null
? Because — and this reality sneaks up in other important places in Velocity — there is no four-letter keyword null
! The null
-ability of injected data is honored, even favored, in Velocity, like by $display.alt
as noted above. But it doesn't have a keyword to create new null
values easily.
So you have to find a roundabout way of getting a reference to a null value. Elsewhere on the net, people say “just use a variable you didn't #set
anywhere else, like $abcdefg
, as that will naturally be null
.” The flaw in this reasoning is nothing actually stops someone else (either a future coder or a current collaborator) from using $abcdefg
for something else. So I prefer to use a reference that cannot exist even by coincidence.
Notes
[1] In the Marketo Lead/Person world, you aren't gonna run into literal null
values, but rather empty strings. This is true despite non-filled fields being represented as [null] or NULL in parts of the Marketo UI. You can and will encounter null
with Custom Objects, though. That's the stuff of another post.
[2] Yes, you could smush the VTL into one line. But if you think Dear #if($lead.FirstName.isEmpty())Friend#else${lead.FirstName}#end,
is sufficiently readable, you're made of stronger stuff than me.