# Form Validation and Analytics

# Form validation, analytics and form submission JS handling in Vue apps & in other scripts

  1. Create a callback function, it must be a named function. This function will be called once the form passes validation. To prevent normal form submission the function must always return false. event is never passed through, so we can't call event.preventDefault() and expect the form not to submit naturally.
  2. Make sure that your form doesn't have any other submit handlers and submit button doesn't have any on-click events that deal with submission logic, all logic must happen in a function defined in step 1.
  3. Enable validation on a form & pass through the callback function that will be called when the form validates.

# Vue.js example

Example of a form that submits a call to API, with an HTML version of API parameters added as a fallback.

Form code

  <form class="form--forgot luminateApi" name="lLogonForm" method="post" action="{{ apiPath }}CRConsAPI" v-if="isSendUsernamePwSubmitted === false">
    <p>Please enter the email address associated with this account.</p>
    <div class="form__row">
      <label for="form__email" class="form__label"><span class="form__required">*</span>Email Address</label>
      <input class="form__input" type="email" name="email" id="form__email" v-model="email" required="required" maxlength="60" />
    </div>
    <div class="form__row">
      <button class="button--teal">Continue</button>
      <input type="hidden" name="api_key" value="{{apiKey}}" />
      <input type="hidden" name="method" value="login" />
      <input type="hidden" name="v" value="1.0" />
      <input type="hidden" name="success_redirect" value="{{nextUrl}}" />
    </div>
  </form>

Custom method within a Vue.js component for handling the submission

  sendUsernamePw: function ($form) {
    var myVue = this;

    luminateExtend.api({
      api: 'cons',
      callback: function (data) { myVue.sendUsernamePwCallback(data); },
      data: 'method=login&email=' + encodeURIComponent(this.email) + '&send_user_name=true',
      useHTTPS: true
    });
    myVue.isSendUsernamePwSubmitted = true;

    return false;
  },

Vue.js component ready method that invoked form validation and analytics with a callback function

  ready: function () {
    enableFormValidation('.form--forgot', 'html5', this.sendUsernamePw);
  }

# JavaScript example

Survey submission example, a bit more involved but it is likely to be used throughout.

Form code

  <form class="form form--newsletter-sign-up" id="form--newsletter-sign-up" name="newsletter-sign-up" method="post" action="https://support.savethechildren.org/site/SSurvey">
    <div class="form__row form__row--colums">
      <div class="form__column">
        <label for="form__email" class="form__label"><span class="form__required">*</span>Email Address</label>
        <input class="form__input" type="email" name="cons_email" id="form__email" required="required" maxlength="60" />
      </div>
      <div class="form__column">
        <label for="4368_1441_2_1301" class="form__label"><a href="#mobile-opt-in-language" class="form__label-link" data-s-object-id="Text|†|"><sup></sup></a>Mobile Number</label>
        <input type="tel" name="4368_1441_2_1301" id="4368_1441_2_1301" class="form__input" pattern="[(][0-9]{3}[)] [0-9]{3}-[0-9]{4}" placeholder="(555) 555-5555" data-validation="custom">
      </div>
      <div class="form__column">
        <button class="button--teal">Continue</button>
        <input type="hidden" name="ACTION_SUBMIT_SURVEY_RESPONSE" id="ACTION_SUBMIT_SURVEY_RESPONSE"  value="Submit Survey" />
         <input type="hidden" name="cons_info_component" id="cons_info_component" value="t" />
         <input type="hidden" name="SURVEY_ID" id="SURVEY_ID" value="1441" />
      </div>
    </div>
  </form>

Form submission callback function which handles survey submission to Luminate

surveySubmitHandler($form) {
  var email = $form.find('input[name="cons_email"]').val();
  var formData = $form.find(':input:not(:hidden)').serialize();
  var formSource = 'NEWSLETTER';
  var sourceCode = $form.find('input[name="source"]').val();
  var subSourceCode = $form.find('input[name="sub_source"]').val();
  var surveyId = $form.find('input[name="SURVEY_ID"]').val();
  // prep form for API submission
  formData = 'survey_id=' + surveyId + '&' + formData;

  // Add source codes to the form data
  if (typeof sourceCode !== 'undefined') {
    formData += '&source=' + sourceCode;
  }
  if (typeof subSourceCode !== 'undefined') {
    formData += '&sub_source=' + subSourceCode;
  }

  // Remove any previous errors, set by the API
  $form.find('.form__error').remove();

  // Submit the value to Cheetah mail as well, use existing function
  submitPixelToCheetahMail(formSource, email);

  // We'll use existing Luminate API library for simplicity
  luminateExtend.api.request({
    'api': 'survey',
    'data': 'method=submitSurvey&' + formData,
    'callback': function (data) {
      if (typeof data.errorResponse === 'undefined'){
        $form.addClass('js-hidden');
        console.debug('form has submitted successfully, hiding it');
      }
    },
    'requiresAuth': true,
    'useHTTPS': true
  });
}

Code execution on page load that enables form validation and analytics with a submit callback

  $(document).ready(function () {
    enableFormValidation('.form--newsletter-sign-up', 'html5', surveySubmitHandler);
  });

# Form analytics without validation on AEM or Luminate pages

Form code and function code surveySubmitHandler are the same as in the previous example.

Attach each analytics event individually

$(document).ready(function () {
  var $form = $('.form--newsletter-sign-up');

  if ($form.length > 0) {
    // Set form into data layer
    setAnalyticsForm($form);
    // Enable form abandonment
    formAbandonmentAnalytics($form);
    // Set marketing source codes
    $form.setMarketingSourceCodes();
    // Update the last touched field
    $form.on('change', 'input', function (event) {
      var $form = $(event.currentTarget);
      // if page has more than one form on it, update the form in the data layer to the last touched one
      setAnalyticsForm($form);
      // update the last touched input
      setFormLastField($form);
    });
    // Attach an on submit event, here calling a function defined in a  previous example
    $form.on('submit', function (event) {
      var $form = $(event.currentTarget);

      surveySubmitHandler($form);
    });
    // Not handling errors are we expect the browser to properly validate input.
    // Server error handling can be handled in the callback function to luminateApi call.
  }
});

# Form analytics on third-party scripts inserted on AEM page

In this example we use Opt-in Monster scripts

/**
 * Helper function for recording responses in LUminate
 * @param  {JSON Object} data  Response provided by Opt-in Monster
 */
function recordResponsesInLuminate(data) {
  var email = ''; // TODO get value from response
  var firstName = ''; // TODO get value from response
  var lastName = ''; // TODO get value from response
  var surveyId = '1441'; // TODO update to any value
  var surveyFormData = 'surevey_id=' + surveyId;

  if (typeof email !== 'undefined' && email !== '') {
    surveyFormData += '&cons_email=' + email
      + '&cons_first_name=' + email
      + '&cons_last_name=' + email;
  }

 // We'll use existing Luminate API library for simplicity
  luminateExtend.api.request({
    'api': 'survey',
    'data': 'method=submitSurvey&' + surveyFormData,
    'callback': function (data) {
      if (typeof data.errorResponse === 'undefined'){
        console.debug('form has submitted successfully');
      }
    },
    'requiresAuth': true,
    'useHTTPS': true
  });
}

/**
 * Helper function for recording responses in Cheetah mail
 * @param  {JSON Object} data  Response provided by Opt-in Monster
 */
function recordResponsesInCheetahMail(data) {
  var email = ''; // TODO get value from response
  var firstName = ''; // TODO get value from response
  var lastName = ''; // TODO get value from response
  // This function is defined in global scripts
  submitPixelToCheetahMail(formSource, email, firstName, lastName);
}
// Attach listener events
document.addEventListener('om.Campaigns.init', function(event) {
  var campaigns = event.detail.Campaigns;
  var formId = '';
  var formName = '';

  if (campaigns.length > 0) {
    // TODO set form variables to valid values
    setFormAnalyticsData(formId, formName);
  }
});
document.addEventListener('om.Campaign.close', function(event) {
  var campaign = event.detail.Campaign;
  var formId = '';
  var formName = '';
  var lastTouchedField = '';
  // TODO: update test for validity
  if (campaign.length > 0) {
    // TODO set form variables to valid values
    setFormAbandonment(formId, formName, lastTouchedField);
  }
});
document.addEventListener('om.Optin.success', function(event) {
  var data = event.detail.response;
  var campaigns = event.detail.Campaign;
  var formId = '';
  var formName = '';
  var lastTouchedField = '';

  // TODO: update test for validity
  if (typeof data !== 'undefined') {
    // TODO set form variables to valid values
    setFormSubmission(formId, formName, lastFieldName);
    // TODO update as needed
    recordResponsesInLuminate(data);
    // TODO update as needed
    recordResponsesInCheetahMail(data);
  }
});
document.addEventListener('om.Optin.error', function(event) {
  var errorMessage = event.detail.response;
  var errorType = 'sign up error'
  var formId = '';
  var formName = '';
  var lastTouchedField = '';

  if (typeof errorMessage !== 'undefined' && errorMessage !== '') {
    // TODO set form variables to valid values
    setFormError(formId, formName, lastFieldName, errorMessage, errorType);
  }
});

# Additional Validation Options

You can find all available validation option on the validation library site. https://github.com/victorjonsson/jQuery-Form-Validator