Saving the last form post (all fields!) to a Textarea
Here’s something you may not know you wanted... but it’s super-useful for precise personalization. (It’s also a prerequisite for an upcoming HOWTO post that’s very exciting, so I wanted to get it out first.)
As you know, when Marketo gets a form submission, it stores the submitted values — no more, no less — alongside the Filled Out Form activity:
However, this “form-fillout-centric” view is not available anywhere else in Marketo outside of the Activity Log.
Instead, when the Filled Out Form is finished processing, the values in the Marketo database — and the values you operate on in Smart Lists — are a combination of:
- the unchanged values of fields blocked from updates
- the current values of fields that weren’t even on a given form (neither hidden nor visible)
- the updated values of submitted fields that were not blocked from updates
As a result, you can’t act only on what someone just submitted.
This means your Send Alert isn’t necessarily reflecting what a person just did.
And it means “entangled” fields, like UTM params, may be updated piecemeal yet falsely appear to be updated at once. (If that phrasing was strange: I mean utm_medium, utm_campaign, and utm_content could in theory be set by 3 different form posts.)
It’s also frustrating when you want to send form data to a Google Sheet and/or to an enrichment service (via webhook), where the idea is to treat each form fillout as a discrete row/call.
To solve those problems, the code below lets you save all the fields in a form post to a single Textarea field, in JSON or URL-encoded format (or both formats at the same time). Here’s what the result looks like in the UI:
Why URL-encoded?
You may wonder why I even included URL-encoded as an option, since it’s harder for humans to read and seems kind of old-fashioned.
It’s because Velocity has a built-in parser for URL-encoded (i.e. query-string-like) strings: $link.uri
. This means you can easily design custom Alerts. No need to send the Textarea value to Sales as-is.
(You see, despite my constant cheerleading for using Textarea fields as “JSON fields,” Velocity doesn’t really speak JSON but rather something sort of like JSON. Parsing JSON in Velocity is needlessly complex if you’re just storing a list of key=value pairs. JSON is much better for nested objects or arrays, though.)
The code
Grab the code, which hooks the usual Marketo Forms 2.0 JS events:
/*
* Serialize form fields to a single Textarea
* @author Sanford Whiteman
* @version v1.0 2021-08-25
* @copyright © 2021 Sanford Whiteman
* @license Hippocratic 2.1: This license must appear with all reproductions of this software.
*
*/
MktoForms2.whenReady(function(mktoForm) {
mktoForm.onSubmit(function(mktoForm) {
let config = {
// replace with your `Full Form Data` field(s)
fields : {
persistJSON : "persistenceTextAreaFieldJSON",
persistURL : "persistenceTextAreaFieldURL"
},
// form fields you don't want to include (if any)
ignoreKeys : [
/* "SomeUnimportantField" */
],
// form fields you want to remap to other names (think webhooks to other services)
remapKeys : {
/* FirstName : "first" */
},
};
/* -- NO NEED TO TOUCH BELOW THIS LINE! -- */
let alwaysIgnoreKeys = ["munchkinId","_mktoReferrer","_mkt_trk"];
let mktoFieldsObj = {};
let currentValues = mktoForm.getValues();
let persistable = Object.keys(currentValues)
.filter(function(k){
return alwaysIgnoreKeys.concat(config.IgnoreKeys).indexOf(k) == -1;
})
.reduce(function(acc, originalk){
let k = config.remapKeys[originalk] || originalk;
let v;
v = currentValues[originalk] || "";
if(Array.isArray(v)){
v = v.join(";");
}
acc[k] = v;
return acc;
}, {});
if(config.fields.persistJSON){
mktoFieldsObj[config.fields.persistJSON] = JSON.stringify(persistable,null,1);
}
if(config.fields.persistURL){
mktoFieldsObj[config.fields.persistURL] = Object.keys(persistable)
.reduce(function(acc, k) {
let v = persistable[k];
acc.push([encodeURIComponent(k), encodeURIComponent(v)].join("="));
return acc;
}, [])
.join("&");
}
mktoForm.addHiddenFields(mktoFieldsObj);
});
});
There are a few configurable options:
- The
fields.persistJSON
andfields.persistURL
values are the (SOAP) field names of the Textarea fields for each format. Leave either one blank and it won’t be used. ignoreKeys
is an array of field names you want to exclude from the Textarea.remapKeys
is an object with any field names you want to change on their way into the Textarea. For example, an enrichment service might expect you to send fn instead of the Marketo name FirstName. (In the object, the left-hand-side/key is the real Marketo name, while the right-hand-side/value is the custom name.)