SOLVED

Help with custom React code to add hidden fields to Marketo form

Go to solution
mohitkakkar
Level 1

Help with custom React code to add hidden fields to Marketo form

Hi All, we're facing a very weird and inconsistent behaviour with Marketo hidden fields in regards of capturing the data.

 

Issue started few months back, when we implemented capturing the UTM values as cookies on our website. We created hidden fields in Marketo to track those cookie values and store in Marketo.

 

It was working with a hit or miss. Then we reached out to Marketo and they said, your UTM cookies set after the user cookie consent and till the time marketo form is already loaded and that's why it's not capturing the cookie values in these kind of cases.

 

To overcome this issue, now I check if user has accepted the cookie consent or not, as  soon as consent is accepted, I am firing a event on site, which then loads the marketo form using Form API addHiddenFields method and already populating the hidden fields values by code itself like this:

form.addHiddenFields(utmValues)
If user doesn't accept the cookie consent, I am loading the marketo form without that addHiddenFields code.

 

Now it looks it works fine. All the cookie values I can see on in the form every time form loads on site. Then I did a lot of testing by submitting so many forms on site and then checking in Marketo if values are getting captured, and there as well I didn't see any issue and all values are getting captured fine.

 

Now the issue is, we're still seeing blank values for those hidden fields in marketo every month when we analyse the real user data.

Seems like Marketo is still failing to capture hidden field values each time.

 

Can anyone please help if they have faced these kind of issues before? This is going on for so long now and we really want to get rid of it.

 

Attaching here whole code which is responsibe for rendering the form on our site:

 

import { navigate, useLocation } from "@reach/router";
import React, { useContext, useEffect, useRef, useState } from "react";
import getPathWithQueryParam from "../../../helpers/getPathWithQueryParam";
import ResourcesContext from "src/components/helpers/ResourcesMainPageContext";
import destyleMktoForm from "src/components/helpers/destyleMktoForm";
import styleMarketoForm from "src/components/helpers/styleMarketoForm";
import getCookie from '../../../helpers/getCookieParser';
import createId from "src/components/helpers/createId.js";

/**
 *
 * @param {string} serial - XXX-XXX-XXX serial key unique to each form
 * @param {number} id - number also unique to each form
 * @param {function} callback - function to be called after all the basic styles are set for further styling
 * @param {string} className - className to be added into the form element
 * @param {string} followUpUrl - it contains redirect path for after success of form submission
 * or extra functionality
 * @callBack receives these parameters in order
 *    @param form the `form` object from Marketo API, which returns from whenReady() function.
 *    @param allInputElements -> same as above
 *    @param formElement -> same as above
 *
 * @returns {Object}
 *    @param formElement -> the whole form taken with the function getFormElem() from Marketo API, used to select each input individually
 *    @param allInputElements -> an array with all the input elements inside the form to add common styles for all of them at once
 *    @param formTag -> the <form> HTML tag with the correct ID to be rendered in the DOM
 */
const useMarketoForm = (
  serial,
  id,
  callback,
  className = "",
  gaLabel = "Content",
  isVisible=true,
  followUpUrl = ""
) => {
  const location = useLocation()
  let formElement;
  let allInputElements = [];
  const formTag = <form id={`mktoForm_${id}`} ga-label={gaLabel} className={className}></form>;
  const formSubmissionUniqueID = {"formSubmissionUniqueID": createId()}

  const successRef = useRef();
  const [hasSubmitted, setHasSubmitted] = useState(false);

  const callLoadFormOnlyOneTime = useRef(0);
  const callDataLayerOnlyOneTime = useRef(0);

  const { otBannerDismissed } = useContext(ResourcesContext);

  const addHiddenFields = formId => {
    if (otBannerDismissed) {
      setTimeout(() => {
        const fetchedCookies = getCookie()
        const validFetchedCookies = (typeof fetchedCookies !== 'undefined') && fetchedCookies.filter(obj => {
          const keys = Object.keys(obj);
          return keys.length > 0 && keys[0] !== "" && (keys[0].includes("utm"));
        });
        const utmParams = validFetchedCookies && validFetchedCookies.reduce((acc, obj) => {
          const key = Object.keys(obj)[0];
          const value = obj[key];
          acc[key.replace(/(^|_)./g, s => s.slice(-1))] = `${value}`
          return acc;
        }, {});
        if (utmParams) {
          window.MktoForms2.getForm(formId).addHiddenFields(utmParams);
        }
      }, 1000);
    }
  }

  const renderMktoForm = () => {
    const headScripts = Array.from(document.querySelectorAll("script"));
    const hasMarketoScript = headScripts.some((script) =>
      script.src.includes("//info.accruent.com/js/forms2/js/forms2.min.js")
    );
    if (!hasMarketoScript) {
      const marketoScript = document.createElement("script");
      marketoScript.src="//info.accruent.com/js/forms2/js/forms2.min.js";
      marketoScript.onload = () => {
        loadForm();
      };
      marketoScript.async = true;
      document.body.appendChild(marketoScript);
    } else {
      loadForm();
    }
  }

  useEffect(() => {
        renderMktoForm()
  }, [isVisible, serial, otBannerDismissed])
 
  const loadForm = () => {
    // Render multiple Marketo forms on a single page
    const cookies = getCookie();
    if (window.MktoForms2 === undefined) {
      setTimeout(() => {
        loadForm();
      }, 500);
    }
    if (
      serial &&
      id &&
      window.MktoForms2
    ) {
      if (callLoadFormOnlyOneTime.current === 0) {
        // Setting this ref to 1 makes sure the function `loadform()` runs only once,
        // so there is no duplicate form on the page
        callLoadFormOnlyOneTime.current = 1;
  
        window.MktoForms2.loadForm(
          "//info.accruent.com",
          `${serial}`,
          `${id}`,
          (form) => {
            if(!form){
              console.info("window.MktoForms2.loadForm was passed an empty form object")
              return false;
            }
            // Adding new hidden field to add  unique ID to each submission
            form.addHiddenFields(formSubmissionUniqueID)
  
            formElement = form.getFormElem()[0];
            // set id to checkbox parent 
            formElement?.querySelectorAll('input[type="checkbox"]')?.forEach((element,i) => {
               element?.parentNode.setAttribute("id","mktoCheckbox_"+i);
            });
            // takes all styles off the form
            destyleMktoForm(form);
            if (
              window &&
              window.dataLayer &&
              callDataLayerOnlyOneTime.current === 0
            ) {
              callDataLayerOnlyOneTime.current = 1;
              window.dataLayer.push({
                event: "gaTriggerEvent",
                gaCategory: "CTA Engagement",
                gaAction: "Form Load",
                gaLabel: gaLabel,
              });
              form.onSuccess(function(values, marketoFollowUpUrl) {
                window.dataLayer.push({
                  event: "gaTriggerEvent",
                  gaCategory: "CTA Engagement",
                  gaAction: "Form Submission",
                  gaLabel: gaLabel,
                });
                if(!(id == calendlyFormId)){
                setHasSubmitted(true);
                if(followUpUrl){
                  navigate(getPathWithQueryParam(location, followUpUrl, cookies))
                }
                else if (marketoFollowUpUrl) {
                  window.location.href = marketoFollowUpUrl;
                } else if (successRef.current) {
                  successRef.current.scrollIntoView({
                    behavior: "smooth",
                    block: "center",
                    inline: "nearest",
                  });
                }
              }
                
                return false;
              });
            }
  
            styleMarketoForm(formElement, allInputElements);
  
            // CALL BACK
            if (callback) {
              callback(form, allInputElements, formElement, formTag);
            }
          }
        );
      } else {
        addHiddenFields(id)
      }
    }
  };
  if (serial && id) {
    if (hasSubmitted) {
      return { formElement, allInputElements, formTag };
    } else return { formElement, allInputElements, formTag };
  } else return { formElement: null, allInputElements: null, formTag: null };
};

export default useMarketoForm;

 

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
SanfordWhiteman
Level 10 - Community Moderator

Re: Help with custom React code to add hidden fields to Marketo form

  • Remember to post to the correct area (“Products”) in future. “Dynamic Chat” is for questions about the Marketo Engage Dynamic Chat module.
  • And remember to use the syntax highlighter (“Insert/Edit Code Sample” button) when inserting code. I edited your post this time.

The code you’ve posted is server-side code, with imports we can’t possibly reproduce. Please put up a demo page running your code so we can take a look at it live.

 

From first glance I can say setTimeout is never the way to solve race conditions and is almost certainly the core problem with your code.

View solution in original post

11 REPLIES 11
SanfordWhiteman
Level 10 - Community Moderator

Re: Help with custom React code to add hidden fields to Marketo form

  • Remember to post to the correct area (“Products”) in future. “Dynamic Chat” is for questions about the Marketo Engage Dynamic Chat module.
  • And remember to use the syntax highlighter (“Insert/Edit Code Sample” button) when inserting code. I edited your post this time.

The code you’ve posted is server-side code, with imports we can’t possibly reproduce. Please put up a demo page running your code so we can take a look at it live.

 

From first glance I can say setTimeout is never the way to solve race conditions and is almost certainly the core problem with your code.

mohitkakkar
Level 1

Re: Help with custom React code to add hidden fields to Marketo form

Hi @SanfordWhiteman , thanks for making the updates in the post, this is my first time here, as I am not a Marketo person, and belongs to web development background.

 

Regarding the test page, this can be used - https://accruentdev.gatsbyjs.io/contact-us

Please make sure to use "utmtesting.com" domain if you submits any form, as it will help us tracking the test users.

 

Regarding setTimeOut, if you check the code, it's used in context only, for example, once the user accepts the cookie consent, one event gets dispatched "otBannerDismissed", after this only setTimeOut of 1 second is used.

 

Once again, thank you for your quick reply, looking forward to more insights from your end.

SanfordWhiteman
Level 10 - Community Moderator

Re: Help with custom React code to add hidden fields to Marketo form

Can you explain exactly what case you’re trying to cover with this arbitrary timeout?

// Delay Marketo form render by 1 sec to make sure UTM values are captured

 

SanfordWhiteman
Level 10 - Community Moderator

Re: Help with custom React code to add hidden fields to Marketo form

P.S. Please be mindful of this:

SanfordWhiteman_1-1700551268908.png

 

SanfordWhiteman
Level 10 - Community Moderator

Re: Help with custom React code to add hidden fields to Marketo form

Finally, I’m not getting the cookie consent popup on your page. Isn’t that kind of key to testing?

mohitkakkar
Level 1

Re: Help with custom React code to add hidden fields to Marketo form

@SanfordWhiteman  - I've updated the code and removed the race condition.

 

Now, following things will happen:

- As soon as the react component is triggered, the form will render on the page

- As soon as user will interact with the cookie consent banner, the useEffect hook will trigger because value of "otBannerDismissed" will be true, and renderMktoForm function will be triggered again, which will add the values of UTMs  as hidden fields in the form with this function addHiddenFields (this function waits for 1 sec before adding the hidden fields, so that cookie values are correctly set in the browser).

 

This code works well, I can see hidden values being set all the times in Marketo for all the submissions, but somehow, still for few real users, we are seeing these hidden field values empty.

 

One possible explaination is users are rejecting the cookie consent (in Europian countries), in this case UTM values doesn't get set as cookies, hence not getting captured in hidden fields.

 

I'd like to know your thoughts as well.

 

If below link is not working with cookie consent, adding here the production link: https://www.accruent.com/contact-us?test

Please make sure to use domain as "utmtesting.com" if you are submitting the form.

 

Thanks a lot in advance.

BrunoPacheco
Level 2

Re: Help with custom React code to add hidden fields to Marketo form

Hi @SanfordWhiteman 

I would like to update you regarding the recent adjustments we've made concerning UTM data. Despite these changes, we have observed that approximately 5 to 7% of our total lead volume still lacks UTM parameter data after form submissions. We are trying to determine whether this is a normal occurrence or if it indicates a remaining gap in our code.

This issue is significant as UTM parameters are crucial for tracking the effectiveness of our marketing campaigns and understanding the sources of our leads. The absence of this data in a noticeable percentage of leads raises concerns about the accuracy and completeness of our lead tracking process.

We are seeking insights or experiences from the community: is it common to encounter a small percentage of leads missing UTM data in similar scenarios, or should we investigate further into our system for potential technical oversights? Any feedback or shared experiences in this regard would be highly valuable.

Thank you for your continued support and collaboration.

SanfordWhiteman
Level 10 - Community Moderator

Re: Help with custom React code to add hidden fields to Marketo form


We are seeking insights or experiences from the community: is it common to encounter a small percentage of leads missing UTM data in similar scenarios, or should we investigate further into our system for potential technical oversights? Any feedback or shared experiences in this regard would be highly valuable.

Thank you for your continued support and collaboration.


It is not at all normal to have people land on a page with UTM query params and not have the corresponding fields submitted with the form. If your code is correct, it will capture 100.00% of those cases.

 

Is it possible for an original link to have query params, but for the final URL visited by the user to not have those params? Yes, some misconfigured redirects (misconfigured on your webserver, say to go from a page alias to its “real” name) can end up stripping the query string. Extremely paranoid privacy plug-ins can do the same, making the (correct) assumption that the params are used for tracking and are relatively harmless to remove.

 

But if someone lands on your page with the params intact, you can always transfer those params to form fields.

BrunoPacheco
Level 2

Re: Help with custom React code to add hidden fields to Marketo form

@SanfordWhiteman  Do you offer any type of consulting service that we could hire to assist us with this issue we're having with UTMs? We are still encountering data inconsistencies in our leads.