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 == $context.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 $context.get("0")?

Ah, yes. I don't want to overwhelm you earlier with the details of null checking in Velocity.

$context.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.