Using ActionTag

ActionTag

ActionTag is an easy way to embed forms on any site. Much like Twitter Embedded Timelines, the idea is to use one line of JavaScript and one line of HTML to have a foolproof way to present a form that posts back to your NGP database. Please note that ActionTag does not work on IE9 or older browsers.

Usage

What is it?

ActionTag is our name for our embeddable forms functionality. Embeddable on any page on the web, it will:

  • Make an API call to retrieve that form’s definition (aka which fields should display)
  • Render a fully functional form to the screen
  • Pre-fill that form under certain circumstances
  • Handle submission of that form back to the NGP servers
  • Deal with behaviors that occur after submission such as:
    • Displaying another form as configured NGP
    • Showing a thank you message as configured in NGP
    • Redirect to another URL, as defined in NGP

Getting Started

All that’s necessary to use ActionTag is to drop a special <div> tag on any page along with our JavaScript file. For example:

  <script type="text/javascript" src="https://d1aqhv4sn5kxtx.cloudfront.net/actiontag/at.js"></script>
  <div class="ngp-form" data-id="-9061805400222859264"></div>

We use the class ngp-form to indicate that this is the place where the form should render.

NOTE: any use of ActionTag for processing payment information must be embedded in a TLS-secured page.

To specify which form to render, add the only other required data attribute, the data-id attribute, e.g. data-id="-9061805400222859264". Each form published has a unique id which can be used in this tag. Please note: that some form IDs start with a minus sign (-), as shown in the example above. Do not delete this as it is a part of the form ID.

These are the simplest cases. Below are some more of the data attributes that can optionally be added to the tag to alter its behavior.

Using Query Strings

A query string is something you can add to the end of the URL your form lives at, e.g. www.somesite.com/formurl?foo=bar Primarily, query string values are used to pre-fill values in the form. The general format is that the first bit is the name of the form field, followed by an equals sign and then whatever data you wish to pre-fill that form field with.

The way to add these onto a form is that the first one needs to use a question mark, and other subsequent ones use an ampersand, e.g. www.someurl.com/formurl?fn=John&ln=Adams

To pre-fill forms, we offer the following options:

  • AddressLine1 : add1, e.g. add1=1250 Hancock St.
  • AddressLine2 : add2, e.g. add2=Ste. 202
  • AdditionalContributionValue: AdditionalContributionValue, e.g. AdditionalContributionValue=25 to prefill the additional contribution amount on event forms only
  • Amount : am, e.g. am=5
  • AmountOptions : amtOpts, e.g. amtOpts=3,5,20.16,50,100
    • This sets the radio button options on a contribution page, enabling dynamic ask amounts. To select a particular contribution amount, use Amount, detailed above
  • City : ci, e.g. ci=Boston
  • Country : c, e.g. c=United States
  • EmailAddress : em, e.g. em=john@adams.com
  • Employer : ep, e.g. ep=The United States
  • FacebookProfileUrl : fb, e.g. fb=http://www.facebook.com/JohnAdamsPresident
  • FirstName : fn, e.g. fn=John
  • HomePhone : hp, e.g. hp=6175551234
  • IsRecurring : r, e.g. r=true
  • LastName : ln, e.g. ln=Adams
  • MiddleName : mn, e.g. mn=Quincy
  • MobilePhone : mp, e.g. mp=6175551234
  • Occupation : oc, e.g. oc=President
  • PostalCode : pc, e.g. pc=02144
  • Prefix : p, e.g. p=Mr
  • SpouseName : sn, e.g. sn=Abigail Adams
  • StateProvince : st, e.g. st=MA
  • Suffix : s, e.g. s=Esq.
  • TwitterHandle : tw, e.g. tw=the_real_adams
  • WorkAddressLine1 : w1, e.g. w1=1600 Pennsylvania Ave
  • WorkAddressLine2 : w2, e.g. w2=Oval Office
  • WorkCity : wc, e.g. wc=Washington
  • WorkEmail : we, e.g. we=john@adams.com
  • WorkPhone : wp, e.g. wp=2025551234
  • WorkPostalCode : wz, e.g. wz=20500
  • WorkStateProvince : ws, e.g. ws=DC

There are also elements that may not be visible, but have to do with how the form submission data is entered into the database:

  • Attributed Contact ID : ac, e.g. ac=VN96C3TBX36
    • This either adds or overrides the defined contact record on a form for the contribution attribution.
    • This is also used to preselect a host committee member on event forms that expose that field.
  • Kiosk Mode: kiosk, e.g. kiosk=true
    • Using this query string parameter disables FastAction and ActionProfile integration. This is useful if you are using a form on a tablet or laptop in a public place and do not want the submission information remembered on that device. Notably, if you configure one form to redirect to another form (what we refer to as secondary ask), the form fields on the second fom will not be pre-populated by the submission info from the first form. If you are launching the form with knowledge of who a contact is, kiosk mode does still allow you to prefill form fields using other query string values, e.g. fn=Michelle as documented above.
  • MarketSource : ms, e.g. ms=201409_fb_birthday_v1
    • This can be used to track different ad buys to the same form to compare ROI
  • SourceCodeId : sourceid, e.g. sourceid=57
    • This either adds or overrides the defined source code on the form. This only applies to contribution forms. You must use the source code ID, not the source code name.
  • QuickSign: quick, e.g. quick=true
    • This automatically submits the form and segues to the next action (thanks page, secondary ask, or an arbitrary other URL) if all the required fields are prefilled by either a query string value or ActionProfiles.
    • This does not work for contribution or event forms.
  • Best Ask: ss, e.g. ss=true
    • Enables our Best Ask feature for adjusting your suggested contribution amounts.

Contact NGP support to learn more about what Attributed Contact IDs, Market Sources, and Source codes are.

Using Google Remarketing Codes

Forms with the new NGP are rendered by the client’s browser, and in order to use Google remarketing codes, you have to reconstruct them to load AFTER the form has loaded.

Here’s an example of what Google will give you as a remarketing code:

<!-- Remarketing tags may not be associated with personally identifiable information or placed on pages related to sensitive categories. For instructions on adding this tag and more information on the above requirements, read the setup guide: google.com/ads/remarketingsetup -->
<script type = "text/javascript">
    /*  <![CDATA[ */
    var google_conversion_id = 985021774; <!-- this will vary by client -->
var google_conversion_label = "lXkMCIq98gYQzvrY1QM"; <!-- this will vary by client -->
var google_custom_params = window.google_tag_params;
var google_remarketing_only = true;
/* ]]> */
</script>
<script type="text/javascript" src="https://www.googleadservices.com/pagead/conversion.js">
</script>
<noscript>
  <div style="display:inline;">
    <img height="1" width="1" style="border-style:none;" alt=""
      src="https://googleads.g.doubleclick.net/pagead/viewthroughconversion/985021774/?value=0&label=lXkMCIq98gYQzvrY1QM&guid=ON&script=0" />
  </div>
</noscript>

Notice that the conversion ID and conversion labels are in two places. First in the regular javascript and second inside the <noscript> code block. These are the values you need to copy into the new code snippet.

Here’s the new code you need to use, make sure to grab the conversion ID and label from the code Google provided, and insert them below. Once you’ve updated this snippet with your account information, you can copy it and paste it into the source of the NGP form page by clicking the “source” button and paste.

<script type="text/javascript">
    var img = document.createElement("img");
    var trackUrl = "https://googleads.g.doubleclick.net/pagead/viewthroughconversion/985021774/?value=0&label=lXkMCIq98gYQzvrY1QM&guid=ON&script=0"; <!-- change ID (985021774) and label (lXkMCIq98gYQzvrY1QM) in this line -->
    img.src = trackUrl;
    document.body.appendChild(img);
</script>
<noscript>
    <div style="display:inline;">
        <img height="1" width="1" style="border-style:none;" alt=""
          src="https://googleads.g.doubleclick.net/pagead/viewthroughconversion/985021774/?value=0&label=lXkMCIq98gYQzvrY1QM&guid=ON&script=0" />
        <!-- change ID (985021774) and label (lXkMCIq98gYQzvrY1QM) in this line -->
    </div>
</noscript>

Attributes

Data attributes on the ActionTag div are the way to pass certain data or instructions to ActionTag to influence its behavior.

Since these attributes are part of the div, any CMS or website system can easily write code that will consume ActionTag with the characteristics they desire. This is the way we have written our own Drupal modules in-house.

What follows are the data attributes we currently support:

data-id (required)

It is used to specify the id of the form to render. Only required if data-form-url is not used. Note that an advocacy-related form must use the data-form-url attribute instead. Example, to load a non-advocacy-related form with id 12345:

<div class="ngp-form" data-id="12345"></div>

data-form-url (required)

It is used to specify the url from which the form definition is retrieved. Only required if data-id is not used, or if presenting an advocacy related form, which requires getting the form definition from a different endpoint. Example, to load a form from a url:

<div class="ngp-form" data-form-url="https://api.myngp.com/v2/forms/12345"></div>

For advocacy related forms, you have to request the form definition through our advocacy service, which we creatively call advocator. This url is constructed using the following pattern:

  • Advocacy form proxy url, e.g. https://advocator.ngpvan.com
  • The URL encoded database API endpoint, meaning take the endpoint https://api.myngp.com/v2/forms/ and represent it like this: https%3A%2F%2Fapi.myngp.com%2F%2Fv2%2FForms%2F
  • the form ID, e.g. 12345
  • and finally the string ngpForm at the end

Complete example for an advocacy related form, where we grab the form definition from the advocacy endpoint, with the rest of the form ID URL encoded after:

<div class="ngp-form" data-form-url="https://advocator.ngpvan.com/https%3A%2F%2Fapi.myngp.com%2F%2Fv2%2FForms%2F12345/ngpForm"></div>
``

data-databag

(optional) - Used to specify whether to use NGP VAN ActionProfiles to fill the form fields. In order for this functionality to work, the site using this must be white-listed by NGP VAN. Contact support if you are interested in getting your URLs whitelisted.

Note: databag is the legacy name for ActionProfile, hence the attribute/name mismatch.

  • Default: nobody
  • Options:
    • nobody - disable this functionality
    • everybody - Allow pre-filling of information from any site in the NGP VAN network. Also requires sharing pre-fill data with other sites in the NGP VAN network.

Example, to enable the NGP VAN Profile filling (assuming the site on which this tag is sitting has been white-listed):

 <div class="ngp-form" data-id="12345" data-databag="everybody"></div>

data-endpoint

(optional)- Used to specify a non-default API endpoint for retrieving the form definition and submitting the resulting data. This is used mostly for hitting development servers; though, if your NGP instance is accessed by a URL other than www2.myngp.com, you likely need to use this attribute.

  • Default: https://api.myngp.com

Example, to load the definition and submit to http://developerboxname.local

<div class="ngp-form" data-id="12345" data-endpoint="http://developerboxname.local"></div>

data-formdef-endpoint

(optional) - This attribute defines a different read endpoint from the POST endpoint defined above. Primarily this is used to call the form definitions from Amazon S3, which NGP will automatically update on every form publish. This leads to faster load times for your forms, especially in peak usage situations, so it is recommended for use in every ActionTag implementation.

  • Default: value of data-endpoint attribute.

Example, to read the form definition from Amazon S3 instead of the NGP API servers:

<div class="ngp-form" data-id="12345" data-formdef-endpoint="https://formdefs.s3.amazonaws.com/api.myngp.com"></div>

data-labels

(optional)- Used to specify the placement of the labels when the form is rendered.

  • Default: above
  • Options:
    • inline - available only for the signup form type, it sets the labels as the HTML5 placeholder attribute on the input.
    • above - displays form labels above the form fields with a

Note, the rationale here is that, while labels being above or beside the input can be altered with CSS, making them appear as placeholders is more complicated.

Example, to have labels appear as placeholders:

<div class="ngp-form" data-id="12345" data-labels="inline"></div>

data-resource-path

(optional) - Used to specify location of ActionTag resources such as actiontag.min.css. Primarily used for internal development purposes.

  • Default: https://d1aqhv4sn5kxtx.cloudfront.net

Example, to use /sites/xyz/libraries/actiontag:

<div class="ngp-form" data-id="12345" data-resource-path="/sites/xyz/libraries/actiontag"></div>

data-template

(optional) - This attribute allows you to indicate which template to use. Note: we may deprecate this functionality in the future and consolidate down to just one template.

  • Default: minimal
  • Options:
    • minimal - Minimal form template
    • accelerator - Template used by our Accelerator websites
    • oberon - legacy template for the act.myngp.com theming, and most likely to get deprecated

Example, to load the minimal template set:

<div class="ngp-form" data-id="12345" data-template="minimal"></div>

Callbacks

Overview & Intended Audience

Callbacks are a rather technical feature set, and are not needed for the vast majority of uses of ActionTag. The callbacks described below are powerful, but also probably require some experience doing web development, and are not needed for most uses of ActionTag. Feel free to skip the rest of these docs unless you are a developer.

We have a series of callbacks in ActionTag which allow developers to alter bits of functionality. In order for ActionTag to know about your callback, we have a simple registration process.

First create the global JavaScript variable nvtag_callbacks (but only if it doesn’t already exist). The following snippet accomplishes this:

var nvtag_callbacks = nvtag_callbacks || {};

If your code is within a closure, you will need to explicitly ensure it is declared in the global namespace so ActionTag can find and reference it. Here that’s done with a local reference so that we no longer have to reference it by calling window.nvtag_callbacks:

window.nvtag_callbacks = window.nvtag_callbacks || {};
var nvtag_callbacks = window.nvtag_callbacks;

The last bit is callback specific. Perhaps you want to add a postRender callback to do something after the ActionTag form has been rendered.

  1. Instantiate that callback as an array if it doesn’t yet exist
  2. Create the actual callback function. The arguments to your function are the callback arguments listed below
    • Note, the first argument to a callback is always the name of that callback. This is done in an attempt to allow one function to be registered from multiple callbacks and then allow that function to handle things internally.
  3. Push your callback onto the postRender callback array

    nvtag_callbacks.postRender = nvtag_callbacks.postRender || [];
    // This function can be called anything, we don't care
    var sampleCallback = function() {
     alert("The form has been rendered!");
    }
    nvtag_callbacks.postRender.push(sampleCallback);
    

    Now your callback code will be called by the ActionTag at the time that postRender should happen! (which is after rendering of the form is complete).

Putting it all together:

var sampleCallback = function() {
  alert("The form has been rendered!");
}
var nvtag_callbacks = nvtag_callbacks || {};
nvtag_callbacks.postRender = nvtag_callbacks.postRender || [];
nvtag_callbacks.postRender.push(sampleCallback);

At the right time in the ActionTag process, we will iterate over each of the callbacks in that array and call them.

Technical background

We use our callbacks syntax to put it into the global namespace because ActionTag loads asynchronously. Also, we wish to support multiple callback sources, hence the instructions to not redeclare it over and over again.

Since our callbacks allow you to alter internals of our system, it is possible to make quite a mess of things, sometimes even in potentially dangerous/breaking ways. With great power comes great responsibility. For example, using the alterFormDefinition() callback:

var alterFormDefinition = function(args) {
  // This will blank out the form_elements array thereby effectively
  // breaking this form definition!
  args.form_definition.form_elements = [];
  return args;
};

So always test your callbacks carefully on a page before putting them live.

For the examples given below with each callback we are omitting the following bit:

var nvtag_callbacks = nvtag_callbacks || {};
nvtag_callbacks.postRender = nvtag_callbacks.postRender || [];
nvtag_callbacks.postRender.push(sampleCallback);

Note that this must happen for each callback in order for them to be registered and called by ActionTag.

alterErrors

alterErrors (args)

args is a dictionary with the following keys:

  • val - The current value of this field
  • field_name - The name of this field
  • require_valid - Errors, if any.
  • def - This element’s definition.

This callback is called when checking the error state of a field. It allows you to intercept the error and alter it.

An error should take one of the 2 following states:

  • false - There is no error
  • string - Text description of error

alterErrors example

alterFill

alterFill(args)

args is a dictionary with the following keys:

  • fill_dict - The fill dictionary. Key/value pairs.
  • fill_source - None, BrowserDatabag, PreviousFormState, RecipientDatabag, or QueryString.

This callback is called after the form has completed rendering but before it is filled allowing you to intercept and alter the values which are pre-filled on the form.

Note: In order for this callback to work properly, you must return the args dictionary with your modified form_definition.

alterFormDefinition

alterFormDefinition(args)

args is a dictionary with the following keys:

  • form_definition - The form’s definition, which we define as the JSON blob that composes what the form should look like.

This callback is called anytime the application requests the form definition. This is used throughout the system, both for rendering and for many other mechanics.

Note: In order for this callback to work properly, you must return the args dictionary with your modified form_definition.

Example usage

alterPost

alterPost(args)

args is a dictionary with the following keys:

  • data - The form state / user data.
  • url - The URL to which the data will be posted.
  • form_id - The Oberon ID of the form.
  • form_definition - The form definition.

This callback is called before POSTing data. It is used to alter the data that will be POSTed.

alterRequireValid

alterRequireValid(args)

args is a dictionary with the following keys:

  • val - The current value of this field
  • field_name - The name of this field
  • require_valid - boolean, true if required, false if not.
  • form_def - The form definition.

This callback is called when determining if a field is required for the form to validate.

onSubmit

onSubmit(args)

This callback is called when a form is submitted.

args is an empty dictionary.

postFill

postFill(args)

args is a dictionary with the following keys:

  • view - The ActionTag form view
  • fill_dict - The fill dictionary. Key/value pairs.
  • submit - The function responsible for submitting the form.

This callback is called after the form is filled.

postRender

postRender(args)

args is a dictionary with the following keys:

  • form_definition: The form definition.
  • options: The view’s options.
  • thank: boolean, true if this is a thank you message, false if it is not.

This callback is called after the form has completed rendering but before it is filled. Common use cases are to fire JavaScript behaviors that require the form to be present.

var myPostRender = function(args) {
  alert("The form has now been rendered!");
  return args;
};

Note, multiple things induce the rendering of a form so this will likely be called multiple times. Ensure any code that depends on postRender() is tolerant of that.

Optionally receive the view and alter any options or override any methods on it:

var myPostRender = function(args) {
    console.log(args.form_definition);
    return args;
}

preSegue

preSegue(args)

This hook is called after a successful form submission, but prior to the segue to the next thing.

As a quick primer, there are, at current, 3 different things which can happen after a successful form submission:

  • Display a thank you message
  • Display another form (aka Secondary ask)
  • Redirect to another page anywhere on the internet

preSegue() happens before the transition to this next step so it’s useful for intercepting it and altering the behavior based on the submitted values and/or the response from the server.

It receives two arguments:

  • submitted_values - The values which were submitted across the network
  • response - The response from the server to that submission

segue

segue(args)

This hook is called when a segue is happening.

As a quick primer, there are, at current, 3 different things which can happen after a successful form submission:

  • Display a thank you message
  • Display another form (aka Secondary ask)
  • Redirect to another page anywhere on the internet

We call the transition from one thing to another thing a segue. This callback is called when making that transition.

Note, it is NOT called in case 3 above because we have no control over another page on the internet.

It receives three arguments:

  • formviews - The form views in question
  • thank - boolean, true if this is a thank you message, false if it is not.
  • calling_tag - The view in question.

Third Party Integrations

Google Tag Manager Event Tracking

ActionTag integrates with Google Tag Manager Event tracking so that you can more readily understand how users are interacting with your forms. This integration is available on all form types. It is reccomended that you utilize Universal Analytics in your Google Tag Manager implementation.

Google Analytics Events

This is the data that will be passed to Gogle Analytics via Google Tag Manager upon successful integration. This data will be viewable in the Google Analytics Events reporting interface.

Event Trigger eventCategory eventAction eventLabel eventValue
Form loaded postRender callback ActionTag form type, e.g. Contribution, Petition Form Load Form Template Number of fields in the form
Form fill postFill callback ActionTag form type, e.g. Contribution, Petition Form Fill Form fill source, identical to the value of fill_source during alterFill, e.g. FastAction Number of fields filled
Form submitted preSegue callback ActionTag form type, e.g. Contribution, Petition Form Submit Submit Button text, e.g. Contribute, Donate For ActionTag forms of type Contribution or Event, the monetary amount paid
Form abandoned window.onbeforeunload event ActionTag form type, e.g. Contribution, Petition Form Abandoned Last field focused on (using the name assigned to the input element), e.g. SelectAmount, FirstName Number of fields completed
Additional dataLayer Variables

In addition to eventCategory, eventAction, eventLabel, and eventValue, the following variables are sent to the Google Tag Manager dataLayer when each event fires.

Field Value
eventSourceCodeId The source code that will be applied to the person who submits this form - retrieved from the query string parameter if present, or from the form definition if not present in the query string
eventActivistCodes An array of activist code IDs that will be applied to the person who submits the form
Example

The following object provides an example of data that would be passed into the Google Tag Manager DataLayer when a Contribution form is loaded.

  {
    event: 'FormEvent',
    eventCategory: 'Contribution Form',
    eventAction: 'Form Load',
    eventLabel: 'Minimal',
    eventValue: 12,
    eventActivistCodes: ['3456','8901'],
    eventSourceCodeId: 2345
  }
Utilizing Form Events in your Google Tag Manager Setup

Before completing these steps, ensure that your Google Tag Manager container is installed properly and utilizing the default “dataLayer” on the page which displays the ActionTag form.

  1. In your Google Tag Manager container, create a new Trigger with the trigger type “Custom Event”.
  2. Set the Event name for your new trigger to “FormEvent” (this is case sensitive) and allow the trigger to fire on All Custom Events.
  3. Create four new User-Defined Variables with the Variable type “Data Layer Variable”, with the following Data Layer Variable Names: “eventCategory”, “eventAction”, “eventLabel”, and “eventValue”.
  4. Create a new Universal Analytics Tag.
  5. Set your Tracking ID in your Universal Analytics Tag to the Google Analytics property of your choice.
  6. In your Universal Analytics Tag, select Track Type “Event”
  7. For each Event Tracking Parameter, using the macro selection, input each of the User-Defined Variables that were created in Step 3 to their appropriate slot.
  8. The Tag should have Non-Interaction Hit set to “True”, so that pageviews for ActionTag form interactions are not recorded in duplicate.
  9. Use the preview setting in the Google Tag Manager container to look at one of your forms to debug and confirm the integration is enabled successfully. To do so, submit a form and watch for your new Form Events tag to fire successfully in the Google Tag Manager debugger.
  10. Via the Data Layer tab in the debugger, inspect the data layer to make sure that the correct information has been passed.
  11. Verify that your data appears in the Google Analytics Events section. Note that it may take up to 24 hours for your data to appear.
  12. Publish the container and ensure that it appears on all pages where ActionTag is present.

Ecommerce tracking in Google Tag Manager

Google Tag Manager (GTM) exists to help you load your Google Analytics (GA) and any other arbitrary JavaScript or HTML tags in the same “container”.

Google Analytics also has an Ecommerce reporting feature, designed to mimic a system where items are added to a cart and then the cart is checked out. We have configured ActionTag to fire Ecommerce events on successful contribution and event form submissions. In the case of a contribution form submission, only a single item will appear in the cart for a transaction. In the case of an event ticket purchase, multiple items can will in the transaction if different ticket types are purchased by the supporter.

If you are using Google Tag Manager with Google Analytics on the same site where you embed ActionTag, and you enable Google Analytics Ecommerce collection in both Google Analytics and Google Tag Manager, the Ecommerce data should pass to the appropriate Google Analytics Ecommerce reports with the following configuration steps.

  1. Ensure that Google Tag Manager is installed on your online form, as follows:
    1. In Online Actions: Provide your Google Account ID Number on Step 1 of the Form Builder.
    2. In NGP: Embed the Google Tag Manager source in the body on your form.
    3. If you are embedding ActionTag on your site, you can install a new GTM container on your site or utilize an existing GTM container.
  2. In Google Tag Manager, create a Custom Event trigger type that is set to fire on all custom events with the event name: transaction.
  3. Now create a Universal Analytics tag in Google Tag Manager. Typically Google Tag Manager is used to house a Google Analytics tag to track page views. You can track your Ecommerce data to this same web property. Set the Tracking ID to the same property that tracks pageviews.
  4. While configuring the tag, click “More Settings” and expand “Fields to Set.” In the “Field Name” section enter in “nonInteraction” (case sensitive), and in “Value” enter in “true.” This step ensures that pageviews are not double-counted when form events fire.
  5. Also under “More settings”, expand “Ecommerce” and click the checkbox for “Enable Enhanced Ecommerce Features” as well as the “Use data layer” checkbox.
  6. Once your tag has been configured, trigger the tag using the transaction Event trigger created earlier.
  7. Use the preview setting in the GTM container to look at one of your forms to debug and confirm the integration is enabled successfully. Do this by submitting a form and watching (in the Google Tag Manager debugger) for the Ecommerce tag to fire successfully.
  8. Via the Data Layer tab in the debugger, inspect the data layer to make sure that the correct information has been passed.
  9. Verify that your data appears in the Google Analytics Ecommerce section. Note that it may take up to 24 hours for your data to appear.
  10. Publish the container and ensure that it appears on all pages where ActionTag is present.

Google Tag Manager utilizes a DataLayer in which information is stored in a JavaScript array and sent to the GTM container. The following code snippet is an example of data that would be passed into the GTM DataLayer upon a successful contribution form submission.

dataLayer = [{
  /* Online Ref # */
  'transactionId': '19065325',

  /* Designation Name */
  'transactionAffiliation': 'People for Good PAC',

  /* Transaction Total */
  'transactionTotal': 2600,

  'transactionProducts': [{

      /* Page Type */
      'category': 'ContributionForm',

      /**
       * SKU based upon unique session ID, combined with the
       * individual product transaction.
       * This value does not remain staticly assigned to the item for 
       * future purchases or for purchases by other users;
       * it is unique to the session)
       */
      'sku': '82390',

      /* Unit Price (Transaction Amount) */
      'price': 2600,

      /* Quantity of item (always 1 for a contribution) */
      'quantity': 1, 

      /* Tenant (Account) Name */
      'brand': 'People for Good VA'
  }]
}];

The following code snippet is an example of data that would be passed into the GTM DataLayer upon successful client-side form submission, for an event with multiple tickets.

dataLayer = [{
  /* Online Ref # */
  'transactionId': '19065378',

  /* Designation Name */
  'transactionAffiliation': 'People for Wildlife PAC',

  /* Transaction Total */
  'transactionTotal': 100,

  'transactionProducts': [
    {
      /* Page Type */
      'category': 'EventForm',

      /**
       * SKU based upon unique session ID combined with the
       * individual product transaction.
       * As with contributions, this value does not remain
       * staticly assigned to the item for future purchases
       * or for purchases by other users.
       */
      'sku': '21940',

      /* Unit Price (Transaction Amount) */
      'price': 50,

      /* Number of tickets of this type sold */
      'quantity': 2,

      /* Tenant (Account) Name */
      'brand': 'People for Wildlife'

      /**
       * Ticket name
       * If no ticket name has been provided in the Online Page Manager,
       * defaults to dollar value of ticket, e.g. "5"
       */
      'name': 'Gala VIP Admission'
    },
    {
      /* Page Type */
      'category': 'EventForm',

      /**
       * SKU based upon unique session ID combined with the
       * individual product transaction.
       * As with contributions, this value does not remain
       * staticly assigned to the item for future purchases
       * or for purchases by other users.
       */
      'sku': '21942',

      /* Unit Price (Transaction Amount) */
      'price': 10,

      /* Number of tickets of this type sold */
      'quantity': 4,

      /* Tenant (Account) Name */
      'brand': 'People for Wildlife'

      /**
       * Ticket name
       * If no ticket name has been provided in the Online Page Manager,
       * defaults to dollar value of ticket, e.g. "5"
       */
      'name': 'Gala General Admission'
    }
  ]
}];

For more information on Google Tag Manager usage:

Google Tag Manager Quick Start Guide

Learn more about the Datalayer from Google

Google Analytics Ecommerce Tag Information

Optimizely

Optimizely is used for A/B testing and for altering attributes of a page at load time.

Since ActionTag loads the form after the page loads, Optimizely won’t work for forms rendered by ActionTag unless we ensure the Optimizely code fires after the form markup has actually been rendered.

This is best achieved by adding a postRender() ActionTag Callback in the Optimizely interface to trigger Optimizely to do its thing after the form has been rendered.

var samplePostRender = function (args) {
  window['optimizely'].push(["activate", 204123457]); 
  // the number here is the experiment ID, which you get from the URL bar when creating the experiment/variations
  return args;
};
window.nvtag_callbacks = window.nvtag_callbacks || {};
window.nvtag_callbacks.postRender = window.nvtag_callbacks.postRender || [];
window.nvtag_callbacks.postRender.push(samplePostRender);

The preceding snippet would be added in the Optimizely admin interface under Global JavaScript

Examples

alterErrors

Take the provided error message and adjust it to something else.

Example:

var myAlterErrors = function(args) {
      if (args.field_name === 'FirstName') {
        if (args.errors) {
          args.errors = "YOU MUST GIVE US YOUR FIRST NAME TO PROCEED";
        } else {
          if (args.val !== "Frank") {
            args.errors = "We only accept a first name of Frank";
          }
        }
      }
      return args;
};

var nvtag_callbacks = nvtag_callbacks || {};
nvtag_callbacks.alterErrors = nvtag_callbacks.alterErrors || [];
nvtag_callbacks.alterErrors.push(myAlterErrors);

alterFormDefinition

Simple

A simple use case is to override some attribute of the definition:

// Implementation of an alterFormDefinition Callback
var alterFormTitle = function(args) {
  // Note, changing this title will not make a visual change unless the title is
  // used in the template which it's not by default in ActionTag.
  args.form_definition.title = "This title has been overridden";
  $('.status').html("<pre>Form Title: " + args.form_definition.title + "</pre>");

  // Must return the altered object so it will be used
  return args;
};

Intermediate

A bit more complex example is to alter something within the form definition. Let’s say you want to change the contents of the Header HTML markup. Note, in this example we are using the Underscore module to iterate over the elements

var changeHeaderHtml = function(args) {
  // args is a dictionary with a key of "form_definition"

  // iterate over each element, 
  _.each(args.form_definition.form_elements, function(child, index) {
      if (child.name === 'HeaderHtml') {
          child.markup = "<p>Replaced Markup</p>";
      }
  });

  return args;
};

Advanced: adding a field

Let’s say you want to add a field to the form definition to be collected:

var addSpouseField = function(args) {
  _.each(args.form_definition.form_elements, function(child) {
      if (child.name === 'ContactInformation') {
          spouse_field = {
              name: 'Spouse',
              title: "What is your spouse's name?",
              type: 'textfield',
              weight: 99, // We want it to appear at the bottom of the fieldset
              queryString: 'sp'
          };
          child.children.push(spouse_field);
      }
  });
  return args;
};

Note, for this case, if Spouse is not in the original form definition, the API would not be able to parse it, so you will need to intercept and store that POST value somewhere else.

Also note that since the added definition has a queryString of 'sp', it will fill if the page has a query string argument like: ?sp=Joe

Rearrange fields

Let’s say we want to rearrange some fields. Or perhaps we want to move some fields into another fieldset. The following example covers that case:

var rearrangeFields = function(args) {
  // First add a new fieldset to hold these fields
  var name_fieldset = {
      name: 'Name',
      title: 'Name',
      type: 'fieldset',
      children: []
  };
  var name_index = args.form_definition.form_elements.push(name_fieldset);
  // Decrement as push returns the length, we want reference to the last element
  name_index--;

  _.each(args.form_definition.form_elements, function(child) {
    if (child.name === 'ContactInformation') {
        _.each(child.children, function(element, key, children) {
            if (element.name === 'FirstName' || element.name === 'LastName') {
                // Add to our new name fieldset
                args.form_definition.form_elements[name_index].children.push(element);

                // Remove from the other fieldset
                children[key] = { 'type': 'hidden'};
            }
        });
        child.title = 'Address';
    }
  });
  return args;
};

In this case we are creating a new fieldset for Name, moving the name fields into that fieldset, removing them from their original fieldset (ContactInformation), and changing the title of the Contact Information fieldset to Address to better indicate its purpose.

Redirect based on recurring or contribution amount values

Perhaps you want to change where a donor goes after form submission in case they choose to give over a certain threshold or choose to give a recurring contributon. Using the preSegue callback you can look at the submitted values and make conditional redirects based on that information. Note, in practice you probably only want to use one of these two conditionals, but both are shown in the same example below for expediency:

var nvtag_callbacks = nvtag_callbacks || {};
nvtag_callbacks.preSegue = nvtag_callbacks.preSegue || [];
nvtag_callbacks.preSegue.push(function submitSegue(args) {
  var contribAmount = parseFloat(args.postVals.Amount);
  var isRecurring = args.postVals.IsRecurring;
  if (isRecurring) {
      window.location = "http://www.yourdomain.org/recurring-thanks-page";
  }
  if (contribAmount > 5000) {
      window.location = "http://www.yourdomain.org/big-spender-thanks-page";
  }
  return args;
});

Obviously, you will want to adjust the threshold for high (or low) dollar conditional redirects, as well as the redirection destination in the above examples.

preSegue

Perhaps you want to track the contribution amount in another analytics package. Here’s how you capture it on form submission:

var contribAmount;
var submitSegue = function (args) {
    contribAmount = args.postVals.Amount;
    console.log(contribAmount);
    return args;
}
var nvtag_callbacks = nvtag_callbacks || {};
nvtag_callbacks.preSegue = nvtag_callbacks.preSegue || [];
nvtag_callbacks.preSegue.push(submitSegue);

You could then call the variable contribAmount to pass off to your analytics service.

Alter country list for disputed territories

If you are working with supporters who identify as living in a disputed territory, perhaps you wish to update the country list. Here’s a quick example of inserting a mythical country “Donkeyland” in the alphabetically correct place in the list of country options, using the postRender callback

var alterCountries = function(args) {
  var previousCountry = document.querySelector('select[name=Country] option[value="Dominican Republic"]');
  previousCountry.insertAdjacentHTML('afterend','<option value="Donkeyland">Donkeyland</option>');
}
var nvtag_callbacks = nvtag_callbacks || {};
nvtag_callbacks.postRender = nvtag_callbacks.postRender || [];
nvtag_callbacks.postRender.push(alterCountries);