I've been using Sanford Whiteman's excellent resource on using JSON in Velocity script but am stuck on how I'd go about only displaying certain records from the JSON.
For example in the following I only want to list the relevant values where {{lead.email domain}} = allProjects.domain. So anyone with a domain of company01.com would see the first 2 records listed, whereas company02.com would only see the last record (there will be about 50 - 100 records in the JSON eventually). Any pointers greatly appreciated!
#set( $allProjects = [
{
"domain": "Company01.com",
"category": {
"URL": "https://www.example.com/project1",
"companyName": "Company 01",
"name": "Example Project Name 01",
"participantType": "Champion"
}
},
{
"domain": "Company01.com",
"category": {
"URL": "https://www.example.com/project2",
"companyName": "Company 01",
"name": "Example Project Name 02",
"participantType": "Champion"
}
},
{
"domain": "Company02.com",
"category": {
"URL": "https://www.example.com/project1",
"companyName": "Company 02",
"name": "Example Project Name 01",
"participantType": "Champion"
}
}
}
] )
<p><br /></p>
<ul>
#foreach( ${allProjects.domain} in $allProjects )
<li>
<a href="${allProjects.category.URL}" target="_blank" id="">${allProjects.category.name}</a></li>
#end
</ul>
Solved! Go to Solution.
There's no built-in collection filtering w/predicates in Velocity. So you do a simple loop and move the matches into another collection:
#set( $domainProjects = [] )
#foreach( $project in $allProjects )
#if( $project.domain.equalsIgnoreCase($lead.emailDomain) )
#set( $void = $domainProjects.add($project) )
#end
#end
#foreach( $project in $domainProjects )
## this is your subset of matches
#end
Your {{lead.email domain}} token, when you check it off and drag it onto the Velocity canvas, isn't going to have a space in it (the Velocity $lead.property name isn't the same as the token name). So I've used $lead.emailDomain here.
There's no built-in collection filtering w/predicates in Velocity. So you do a simple loop and move the matches into another collection:
#set( $domainProjects = [] )
#foreach( $project in $allProjects )
#if( $project.domain.equalsIgnoreCase($lead.emailDomain) )
#set( $void = $domainProjects.add($project) )
#end
#end
#foreach( $project in $domainProjects )
## this is your subset of matches
#end
Your {{lead.email domain}} token, when you check it off and drag it onto the Velocity canvas, isn't going to have a space in it (the Velocity $lead.property name isn't the same as the token name). So I've used $lead.emailDomain here.
Once you get the #foreach working, you may then be surprised by an obscure system behavior with links and #foreach loops.
If you are going to output a link from within a #foreach loop, you will probably need to add the class "mktNoTrack" to your link OR deploy a workaround. Otherwise, you'll potentially output a dead links.
Here's a resource that helped me understand what was going on when encountering this issue: https://blog.teknkl.com/multiple-marketo-tracked-links-in-velocity/
Thanks for the tip, Mark! I am planning to output links so this is very helpful.
Ouch. That's kind of a big bug, at least for what I had planned. Is this something Marketo plan to fix?
There's no plan to fix it, but you can work around it easily (if clumsily) by preallocating up to a max # of variables, like in my blog post.
There are also some situations in which the bug surprises me by not kicking in. I went so far as to think the behavior had been changed a few months ago... but no, it was because of one exception-to-the-exception. You might as well assume it's always a problem.
Thanks, Sanford - I really do appreciate all of your resources on this. But, as a novice with VTL, when you have to use a giant If statement to use a forEach, I'm thinking I may as well just use a giant If statement without the forEach.
It feels like Marketo have dropped the ball on this one. Surely multiple (tracked) links from COs would be a common use case for VTL?
It's still better to use a #foreach (and I always do).
The only object property that would require special treatment is one that is used as an href. So you can do all your filtering, mapping, etc. with #foreach loops, and keep using $object.property for output of all other properties. Then at the end create N standalone String $references to hold the URLs.
'Then at the end create N standalone String $references to hold the URLs.'
Slightly confused how to join this all up in my example. But will try and tackle this tomorrow, so 'basically' I need to:
Filter the list i.e.
#set( $domainProjects = [] )
#foreach( $project in $allProjects )
#if( $project.domain.equalsIgnoreCase($lead.Email_Domain__c) )
#set( $void = $domainProjects.add($project) )
#end
#end
Then create the url reference from the filtered list (with something similar to your code):
#foreach( $key in $lead.downloadcenter.split(";") )
#set( $downloadLink = $allDownloadLinks[$key] )
#evaluate( "${esc.h}set( ${esc.d}downloadLink_${foreach.count} = ${esc.d}downloadLink )" )
#end
Then have an if statement for N url placeholders, where if the link exists in the filtered list it displays, otherwise it doesn't.
(And I thought tomorrow would just be a day of colouring in this email!)##
You can do it without the #evaluate, but it still involves repetition where we'd prefer DRY.
Here's an example, also including capping the number of URLs at 8 to make sure there's no unexpected error.
#foreach( $idx in [0..$math.min(7,$math.sub($domainProjects.size(),1))] )
#set( $project = $domainProjects[$foreach.index] )
You're a ${project.category.participantType} for Project ${foreach.count}:
#if( $foreach.index.equals(0) )
#set( $project_category_URL_0 = $domainProjects[0].category.URL.substring(8) )<a href="https://${project_category_URL_0}">##
#elseif( $foreach.index.equals(1) )
#set( $project_category_URL_1 = $domainProjects[1].category.URL.substring(8) )<a href="https://${project_category_URL_1}">##
#elseif( $foreach.index.equals(2) )
#set( $project_category_URL_2 = $domainProjects[2].category.URL.substring(8) )<a href="https://${project_category_URL_2}">##
#elseif( $foreach.index.equals(3) )
#set( $project_category_URL_3 = $domainProjects[3].category.URL.substring(8) )<a href="https://${project_category_URL_3}">##
#elseif( $foreach.index.equals(4) )
#set( $project_category_URL_4 = $domainProjects[4].category.URL.substring(8) )<a href="https://${project_category_URL_4}">##
#elseif( $foreach.index.equals(5) )
#set( $project_category_URL_5 = $domainProjects[5].category.URL.substring(8) )<a href="https://${project_category_URL_5}">##
#elseif( $foreach.index.equals(6) )
#set( $project_category_URL_6 = $domainProjects[6].category.URL.substring(8) )<a href="https://${project_category_URL_6}">##
#elseif( $foreach.index.equals(7) )
#set( $project_category_URL_7 = $domainProjects[7].category.URL.substring(8) )<a href="https://${project_category_URL_7}">##
#end
${project.category.name}##
</a>
<br>
<br>
#end
As you can see, you get to reuse properties like $project.category.participantType, so all is not lost, it's just about the URL values.
Believe me, I dearly wish this were not necessary, but it is because of the way the UberSpector works.