SOLVED

Velocity email scripting - beginner's guide?

Go to solution
Anonymous
Not applicable

Hi everyone!

I'm starting to add email scripts in my emails and I'm looking for a guide for beginners, if this exists somewhere. I understand that Velocity is based on Java and that a Java guide could maybe help as well.

I'm trying to display text based on the Postal Code's first character:

#if(${lead.PostalCode}.substring(0, 0) == "H")

  #set($city = "Montreal")

#elseif(${lead.PostalCode}.substring(0, 0) == "M")

  #set($city = "Toronto")

#else

  #set($city = "your awesome city")

#end

${city}

This is raising an error, so I'm guessing the substring shouldn't be used this way. Any help will be greatly appreciated.

Thomas

1 ACCEPTED SOLUTION
SanfordWhiteman
Level 10 - Community Moderator

#if( $lead.PostalCode.startsWith("H") )

  #set( $city = "Montreal" )

#elseif( $lead.PostalCode.startsWith("M") )

  #set( $city = "Toronto" )

#else

  #set( $city = "your awesome city" )

#end

${city}

  • Don't bother with ${formal notation} in #set directives, where it isn't necessary, makes code harder to read, and can break stuff. You don't need {} here.
  • substring(0,0) means 0 characters starting from index 0. That's not going to match anything.
  • String objects have a startsWith() shortcut that returns a boolean, so you don't need to create a new string (which is what substring() does).

Also, think about moving to a collection-first approach as I describe here. With Velocity, organization is precious (it's not a particularly easy-to-read language, mostly because of the need to reduce whitespace).  So having all your "magic strings" at the top of a script is helpful. Like so:

#set( $pcInitialToCity_xref = {

  "H" : "Montreal",

  "M" : "Toronto",

  "" : "your awesome city"

} )

#set( $pcInitial = $lead.PostalCode.substring(0,1) )

#set( $city2 = $pcInitialToCity_xref[$pcInitial] )

#if( !$city2 )

  #set ( $city2 = $pcInitialToCity_xref[""] )## default

#end

${city2}

  • In this case, I do use substring() because I need that single-character string to do a property key lookup.

View solution in original post

19 REPLIES 19
Anonymous
Not applicable

Do you put the java script directly in the source code of the email you are working with?

SanfordWhiteman
Level 10 - Community Moderator

Not JavaScript: Velocity (VTL)! Very, very different.

As I mentioned on your other thread, VTL goes in a token. Then the token name goes in your email.

Luke_Wotton
Level 4

Sanford, awesome work.

Question - can you put tokens inside a script token?

Example.

If foo = bar

token = {{my.token1}}

else

token = {{my.token2}}

Or does Marketo not evaluate tokens inside tokens?

SanfordWhiteman
Level 10 - Community Moderator

Generic text/HTML tokens, no.

Other Velocity token values, yes (though you don't need to reference them as {{my.tokens}} but as simple Velocity vars).

When you use Velocity a lot, you tend to stop using text tokens and instead work in VTL semi-exclusively.  For example, instead of a text token

  twitter.com/ourprimaryhandle

you have a Velocity token

#set( $globals = {

   "twitterHandle" : "twitter.com/ourprimaryhandle",

   "facebookPage" : "facebook.com/ourpage"

} )

Then include the {{my.globals}} token (in the template, typically) and you have access to to $globals.twitterHandle in any subsequent script.

SanfordWhiteman
Level 10 - Community Moderator

#if( $lead.PostalCode.startsWith("H") )

  #set( $city = "Montreal" )

#elseif( $lead.PostalCode.startsWith("M") )

  #set( $city = "Toronto" )

#else

  #set( $city = "your awesome city" )

#end

${city}

  • Don't bother with ${formal notation} in #set directives, where it isn't necessary, makes code harder to read, and can break stuff. You don't need {} here.
  • substring(0,0) means 0 characters starting from index 0. That's not going to match anything.
  • String objects have a startsWith() shortcut that returns a boolean, so you don't need to create a new string (which is what substring() does).

Also, think about moving to a collection-first approach as I describe here. With Velocity, organization is precious (it's not a particularly easy-to-read language, mostly because of the need to reduce whitespace).  So having all your "magic strings" at the top of a script is helpful. Like so:

#set( $pcInitialToCity_xref = {

  "H" : "Montreal",

  "M" : "Toronto",

  "" : "your awesome city"

} )

#set( $pcInitial = $lead.PostalCode.substring(0,1) )

#set( $city2 = $pcInitialToCity_xref[$pcInitial] )

#if( !$city2 )

  #set ( $city2 = $pcInitialToCity_xref[""] )## default

#end

${city2}

  • In this case, I do use substring() because I need that single-character string to do a property key lookup.
Anonymous
Not applicable

Hi again Sanford,

Unfortunately I can't get your script to work. I'm just trying to get the 1st character of the Postal Code for now. So I simplified the code to:

#set( $pcInitial = $lead.PostalCode.substring(0,1) )

I get this error:

An error occurred when procesing the email Rendered_Email_Velocity_Error_Area_?!

Invocation of method 'substring' in class java.lang.String threw exception java.lang.StringIndexOutOfBoundsException: String index out of range: 1 near

?

If I drag and drop the Postal Code field from the right hand column and use the syntax Marketo suggests:

#set( $pcInitial = ${lead.PostalCode}.substring(0,1) )

I get a different error message:

An error occurred when procesing the email Body!

Lexical error, Encountered: "s" (115), after : "." at *unset*[line 187, column 231] near

The YP Dine Team<br>

<br>

P.S. The newsletter is still marinating. For op

Which makes me wonder, are the 2 ways to write the variables really equal?

Thanks,

Thomas

SanfordWhiteman
Level 10 - Community Moderator

An error occurred when procesing the email Rendered_Email_Velocity_Error_Area_?!

Invocation of method 'substring' in class java.lang.String threw exception java.lang.StringIndexOutOfBoundsException: String index out of range: 1 near

That's what happens when you run ​substring() ​and one of the indices is longer than the string length. You need to wrap in a condition like

  #if( !$lead.PostalCode.isEmpty() )

to short-circuit this.

One of the principal differences across Java String methods is whether they barf if the object is empty, or if it's not a String object.

Velocity complicates matters further by being permissive with ​nonexistent variables, ​which Java never is, but still not permissive with indexes! For example:

  #set( $a = "" )

  $!{b.substring(0,1)} <-- no error!

  $!{a.startsWith("ABCDE")} <-- no error!

  $!{a.substring(0,1)} <-- error!

If I drag and drop the Postal Code field from the right hand column and use the syntax Marketo suggests:

  1. #set($pcInitial=${lead.PostalCode}.substring(0,1))

This syntax is broken -- as I noted before, don't use ${formal notation} outside of quotation marks as you will eventually (though, confusingly, not  always) create syntax errors. In a #set directive you just use $lead.PostalCode.

Anonymous
Not applicable

Thanks Sanford,

It works now when I wrap it with #if( !$lead.PostalCode.isEmpty() )

I was surprised I had to use this even though I was testing with a lead that *had* a Postal Code... *sigh*

Cheers,

Thomas

SanfordWhiteman
Level 10 - Community Moderator

You wouldn't need it when the field has a value, but it is an important guard in all cases.

Anonymous
Not applicable

I can assure you that the following code (with the test commented) raises an error:

#set( $pcInitialToCity_xref = {

  "H" : "Montreal",

  "J" : "Montreal",

  "M" : "Toronto",

  "V" : "Vancouver",

  "" : "your city"

} )

#set( $pcInitial = $lead.PostalCode )

##if( !$pcInitial.isEmpty() )

  #set( $pcInitial = $pcInitial.substring(0,1) )

##end

#set( $city = $pcInitialToCity_xref[$pcInitial] )

#if( !$city )

  #set ( $city = $pcInitialToCity_xref[""] )## default

#end

${city}

Although I send it to the following lead:

Screen Shot 2017-01-09 at 1.05.57 PM.png

So that had me searching for a while! It looks like Marketo looks at distinct values of that field even for other leads before interpreting?

Maybe this only happens when sending out sample emails?

Thomas

SanfordWhiteman
Level 10 - Community Moderator

Send Sample shouldn't be used to test Velocity... send real emails.

Anonymous
Not applicable

Good day Sanford,

May I disturb you once more to ask if this is the way you would proceed to personalize a subject line? Thanks!

Thomas

#if(!$lead.FirstName.isEmpty())

  #set($subject = "$lead.FirstName, beat grocery stores at their own game with our new hit app")

#else

  #set($subject = "Beat grocery stores at their own game with our new hit app")

#end

${subject}

SanfordWhiteman
Level 10 - Community Moderator

Sure, LGTM. The only thing I might change is change to ${formal.notation} in the #set. FN tends to be overused in comparisons and when calling methods -- and can actually break stuff there, as we've seen. But it's underused within quotation marks, where it adds clarity and is a net good.

Anonymous
Not applicable

Thanks Sanford, I changed the notation between quotation marks accordingly.

Thomas

Nicholas_Manojl
Level 9

Or the 'Preview Email / View by Lead' function.

(when this is the most helpful thing you can add to a thread because the rest has been covered off so thoroughly)

Anonymous
Not applicable

Thanks Nicholas,

Actually I didn't know that this was possible. I used to use this feature in Mailchimp, very helpful.

Have a great day,

Thomas

Anonymous
Not applicable

Thanks Sanford, that was very useful.

I subscribed to your blog!

For Substring I was referring to the wrong language I guess:

String (Java Platform SE 7 )

Where the second parameter in fact is the last index, not the length. Anyway... wrong language

I agree for the collection management. The code snippet I gave only showed a part of the postal codes I will manage, so going with the collection is the right was to go indeed.

Can you show me in the official docs where they mention startsWith()? I'm ashamed I missed that.

Thanks

SanfordWhiteman
Level 10 - Community Moderator

Java String::substring() is being called, but the second index is exclusive. Thus in the particular case of (0,0) I was illustrating that you're never going to fetch any characters.

Technically it's "from index 0 to index 0, including index 0 but not including index 0," but this phrasing might make you think (since the order of operations within substring() is actually undocumented) that you'd still get the character at index 0.

SanfordWhiteman
Level 10 - Community Moderator

There's nothing more beginner-y than the official docs. There's also an old book, Mastering Jakarta Velocity, which is worth picking up, even if 66% of it is aimed at Java programmers (that is, people implementing Velocity into their Java projects, as opposed to mere "consumers" of existing Velocity installations, like Marketo users).

If you aren't following my blog posts on Velocity, you should.

I wouldn't say Velocity is "based on" Java (though you may be quoting the old me there!). It's written in Java, and as such can consume a subset of the Java native API. (On a good day, with the right project, I call it a "powerful subset"; on a bad day, I call it a "an embarrassingly small subset." It depends on what you're trying to do.)  

VTL is a standalone, lightweight template language that definitely isn't Java. But its separate syntax (# to start directives, for example) makes it easier for (some) Java syntax to be embedded directly in VTL without causing parsing errors.