To switch up the Nancy Sinatra song, Booleans keep truthin’, when they ought to be falsin’.
As explored in earlier blog posts, when Marketo exposes Boolean fields in Velocity — fields on the Lead/Person object, not on other objects — they become Strings, not real Booleans.
And they're not even very Boolean-like Strings: they have values "1"
and ""
(the empty string) which in Velocity are both truthy values.[1]
As a result, you can't use a standard Boolean expression…
#if( $isCustomer )
You're a customer.
#else
Wouldn't you like to be a customer?
#end
… because everyone will match the first condition.
You have to be more exact, unfortunately making your code less self-documenting:
#if( $isCustomer == "1" )
You're a customer.
#else
Wouldn't you like to be a customer?
#end
Now, this more verbose version may not seem like a big deal, but I consider it to be poor programming practice because it relies on a “magic string”: a person reading your code has no way to know that "1"
has some special significance and that the variable could not hold any other string value. That is, any Boolean-ish thing should be an enumeration only allowing 2 values (one representing true and one representing false, whatever those values might be) but since it's a freeform String it has no such restriction.
So here's something you can add to your global {{my.velocityIncludes}} token. (You do have such a token, don't you? All the cool kids do.)
#set( $mktoBoolean = { "1" : true, "" : false } )
With that one-time include (put it in the <head>
of your templates) now you can refer to those Boolean-ish fields like so:
#if( $mktoBoolean[$isCustomer] )
You're a customer.
#else
Wouldn't you like to be a customer?
#end
Now it's clear that you're using the variable as a Boolean.
I've recently decided this simple method is good enough. In the past I'd been using a list (manually maintained in {{my.velocityIncludes}}) of known Boolean field names, then “massaging” those fields on the lead to turn them into real Booleans before using them. But that takes prep work and IM(new)O isn't worth it.
Code breakdown (if you need it)
The snippet above is just the kind of thing that can make new devs think VTL syntax works a certain way, and then try to adapt it to other scenarios only to find syntax errors.
So let me explain exactly what's happening, as short as it is.
First, let me add line breaks for readability:
#set( $mktoBoolean = {
"1" : true,
"" : false
} )
By using Velocity's map literal syntax we're creating a simple Map object with 2 keys: "1"
and the empty string ""
.
(Informal/imprecise terms for such an object are Hash, HashTable or Dictionary, and the exact type is LinkedHashMap. Also feel like noting that even though Velocity's map literal syntax looks the same as JavaScript's object literal syntax, it creates an object that is different in one critical way, though that difference isn't relevant here.[2])
The keys in the Map can have any values, even including null
, and can certainly include any kind of string, including an empty string. You access the keys in a Map using .get
, bracketed-property syntax or, when it can be parsed unambiguously, dot-property syntax.
So for a more general example, if we defined this Map:
#set( $someOtherMap = {
"FaveFruit" : "apple",
"FaveVeg" : "broccoli"
} )
Then we can use one of 3 equivalent ways to access the person's favorite fruit:
$someOtherMap.get("FaveFruit")
$someOtherMap["FaveFruit"]
$someOtherMap.FaveFruit
Those all address the same key and will all show apple
.
In the specific case of the $mktoBoolean
Map, we can't use the 3rd option of dot-property syntax though, because
$mktoBoolean.1
isn't a valid expression in Velocity Template Language since it starts with a number. We're limited to
$mktoBoolean.get("1")
$mktoBoolean["1"]
This limitation isn't a big or surprising deal, by the way. Just one of a zillion cases where certain accessing syntax might be unusable, but that doesn't mean the initial definition of the variable was wrong. Sometimes you end up limiting the ways to refer to object keys — another common case is when a string key has a space in it ({ "My Other Car" : "Lambo" }
), which also doesn't work with dot-syntax so you have to use $someOtherMap["My Other Car"]
or $someOtherMap.get("My Other Car")
— but you get other benefits in return.
Aaaaanyway, so we have a Map with 2 keys, both Strings. And the value of each key is a real Boolean: I used the literal Boolean values true
and false
, not Strings.
That means we can use bracket-syntax to access the corresponding key in the Map, which will return a Boolean. When I do
#if( $mktoBoolean[$isCustomer] )
I'm getting the value from the $mktoBoolean
Map that matches the key $isCustomer
. That is, within our reserved world of "1"
and ""
, I'm always getting either $mktoBoolean["1"]
or $mktoBoolean[""]
, and those are both Booleans so the result can be used clearly and consistently.
Hope that all made sense!
Notes
[1] In Velocity's underlying Java environment, these would both be String instances and thus neither true
nor false
; they'd throw fatal errors if you tried to use them as Booleans. (Unlike other languages you might have used, in pure Java only an actual java.lang.Boolean
can be used in a Boolean expression; there's no concept of truthy or falsy strings or numbers.)
But VTL is an often frustrating “friendlier” dialect on top of Java which minimizes visible errors. In VTL, #if ($someStringVariable)
won't chuck an error message into your email; yet the logic it uses is basically “everything but exact Boolean false
or null
is true” which can be very misleading.
[2] That way being that the keys in a LinkedHashMap are ordered. Key order has no bearing on the way $mktoBoolean
is used in this scenario, but it's a classic source of confusion in object-land. In JavaScript, object literal syntax creates a plain JS Object
, which is unordered and is roughly like a simpler Java HashMap
(https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html).