Velocitip: A syntax slip might not throw a fatal error, but can be a big deal

SanfordWhiteman
Level 10 - Community Moderator
Level 10 - Community Moderator

Take this VTL snippet, intended to output trial keys for known combos of Country and custom field Intended Use:

#if( $lead.Intended_Use__c.equals("MycoAlert Trial Kit")) && ($lead.Country.equals("United States") )
KGFV-WJN7-CW43
#elseif( $lead.Intended_Use__c.equals("MycoAlert Plus Trial Kit")) && ($lead.Country.equals("United States") )
7XFC-WFZP-CTDY
#elseif( $lead.Intended_Use__c.equals("MycoAlert Trial Kit")) && ($lead.Country.equals("Austria") )
VTQF-RL0D-Q6NJ
#elseif( $lead.Intended_Use__c.equals("MycoAlert Plus Trial Kit")) && ($lead.Country.equals("Austria") )
QZIC-L36N-2BDQ
#end

 

At a glance, what do you think the output will be for this lead:

SanfordWhiteman_0-1711259374107.png

 

Now click below to reveal the actual output:

Spoiler
  && (false )
KGFV-WJN7-CW43

Not what you expected?

 

Perhaps you counted the right and left parentheses, and indeed there are four opening ( and four closing ) on each line. But you missed that:

  • the && “operator” isn’t an operator here: it’s outside of the #elseif() directive, so it’s just just two ampersand characters with no special meaning
  • $lead.Country.equals("United States") is also outside the #elseif(), so it’s treated as standalone output

 

See, Velocity — like all template languages — centers on output. So anything between #-directives or $-references, as long as it doesn’t otherwise violate VTL grammar, is assumed to be a literal string.

You couldn’t get away with that wandering && in non-template languages. In JS, for example, this is a fatal syntax error:

if( lead.someProperty === "Some Value" ) { &&
}

 

Making it work

Here’s the code you probably thought you were seeing above:

#if( $lead.Intended_Use__c.equals("MycoAlert Trial Kit") && $lead.Country.equals("United States") )
KGFV-WJN7-CW43
#elseif( $lead.Intended_Use__c.equals("MycoAlert Plus Trial Kit") && $lead.Country.equals("United States") )
7XFC-WFZP-CTDY
#elseif( $lead.Intended_Use__c.equals("MycoAlert Trial Kit") && $lead.Country.equals("Austria") )
VTQF-RL0D-Q6NJ
#elseif( $lead.Intended_Use__c.equals("MycoAlert Plus Trial Kit") && $lead.Country.equals("Austria") )
QZIC-L36N-2BDQ
#end

 

Now, the && is an actual operator and $lead.Country is part of the condition.

 

Making it shorter and better

The fixed code above will work, but it’s hard to maintain as string constants are peppered throughout the code.

 

Better to use a map-first approach where you put all the strings up top, leaving you with only one line of “code” proper!

#set( $trialKeyPatterns = {
  ["MycoAlert Trial Kit","United States"] : "KGFV-WJN7-CW43",
  ["MycoAlert Plus Trial Kit","United States"] : "7XFC-WFZP-CTDY",
  ["MycoAlert Trial Kit","Austria"] : "VTQF-RL0D-Q6NJ",
  ["MycoAlert Plus Trial Kit","Austria"] : "QZIC-L36N-2BDQ"
})
${trialKeyPatterns.get([$lead.Intended_Use__c, $lead.Country])}

 

This works because the keys of a Java LinkedHashMap use value equivalence (equals), rather than reference equality (==). So map.get(key) finds the item whose key equals(key). Here, key is an ArrayList, and ArrayList.equals means “Are all indexes and items equal?”

 

In other words:

#set( $listA = ["MycoAlert Trial Kit","United States"] )
#set( $listB = ["MycoAlert Trial Kit","United States"] )
#set( $areTheyEqual = $listA.equals($listB) ) ## true
#set( $listC = ["United States","MycoAlert Trial Kit"] )
#set( $areTheyEqual = $listA.equals($listC) ) ## false
#set( $mapD = {
  $listA : "hello"
} )
#set( $getKey = $mapD.get($listA) ) ## "hello"
#set( $getKey = $mapD.get($listB) ) ## "hello"
#set( $getKey = $mapD.get($listC) ) ## null
 
 
NOTES

✱ Yes, the terms are kind of confusing, even more so when you consider that Velocity ==, which we’re constantly reminding people not to use because it can act weird, isn’t the same as Java ==.

480
1
1 Comment
SaurabhGoyal_GN
Level 4

Nice learning about velocity scripting. Thanks for the guidance as always @SanfordWhiteman .