Campaign Design API > Using CD API on SPA navigation

Who is this article for?

  • Web Developers who build campaigns on Single Page Applications
  • Marketers working closely with Web Developers managing optimization campaigns on Single Page Applications

Overview

Challenge: Navigational differences

Oracle Maxymiser is implemented by loading an HTML JavaScript tag in the head element. The browser calls our JavaScript library during the page load event, so campaign content is delivered and applied automatically even before the actual page content is displayed.

It works seamlessly with traditional web pages as tested areas remain static. Since content on an SPA framework is primarily loaded without new page loads we cannot rely only on the initial page load from the server to run our test campaigns.

Solution: Handle single-page route change

We provide a custom Campaign Design API module to assist marketers and developers design and build test campaigns on single-page websites. It uses HTML5 pushState event instead of the traditional page load trigger.

The module allows to run any custom code on navigation. It was tested and used on Angular (bundled with ng-route), Ember (with Ember.router) and Knockout (with pager.js) on the following browsers:

  • Internet Explorer Versions 10 and 11
  • Safari latest version
  • Chrome latest version
  • Firefox latest version

Getting started

The module must be applied as a site script. Once applied at site level, all campaigns within the site can use the module.

Note: This module can only be used on CD API sites.

Source code:

// Single-page routing module v1.0.3.0
"undefined"===typeof router&&function(){function b(a){a&&c!==a&&(c=a,events.trigger("routeChange",{newUrl:a}))}function e(a){return function(){b(window.location.protocol+"//"+window.location.host+arguments[2]);return a.apply(this,arguments)}}var c;modules.define("router",{autoDefine:!0,singleton:!0},function(){this.onRouteChange=function(a){var b=function(b){for(var c=0;c<a.urls.length;c++){var d=b.newUrl;if(d&&d.match&&d.match(a.urls[c])){a.handler&&a.handler();break}}};b({newUrl:window.location.href});
events.on("routeChange",b);return this};this.triggerRouteChange=function(a){c=a;events.trigger("routeChange",{newUrl:a});return this};this.data={}});history.pushState=e(history.pushState);history.replaceState=e(history.replaceState);window.addEventListener("popstate",function(a){b(window.location.href)});window.addEventListener("hashchange",function(a){b(a.newURL)})}();

You can copy and paste the above code as a site script.

The integration may require customization in the following cases:

  • No URL change is performed when visitor travels between the SPA pages (see manual module triggering below)
  • Internal data structures (e.g. Angular templates, controllers, providers, fabrics, etc.) are modified as part of a test (see manual module extending below)

Module reference

The single-page routing module is automatically defined and becomes available in all campaign and site scripts on pages where the original integration script was mapped. You can access the module's interface using "router" variable.

Methods

onRouteChange( properties ) → {Module}

Assigns custom handler to be triggered on page URL change.

Parameters

Name Description Type
properties

Route change handling properties:

  • urls ← Array {String|RegExp}
    Page URLs you want to run custom handler on
  • handler ← Function
    Custom handler executed when at least one of the URLs you defined is equal to the page URL.
Object

Code example

// campaign T48Nav will be generated when the current
// page URL conforms one of the specified URLs
router.onRouteChange( {
// urls may be described by string or regexp
urls: [ "https://test.com/", /https:\/\/test\.com\//i ],
handler: function() {
renderer
.hide( '.nav' )
.getContent( 'T48Nav' )
.done(function() {
renderer
// waiting is required for the case
// when content has not yet arrived
.waitForElement( '.nav' )
.done(function() {
renderer
.runVariantJs()
.showAll();
})
});
}
} );

triggerRouteChange( targetUrl ) → {Module}

Triggers handlers assigned to the target URL.

Provides a way to programmatically invoke a handler for a target URL.

Parameters

Name Description Type
targetUrl The URL of the assigned handlers you want to be triggered. String

Code example

// the same URL is shared across the whole booking process so we
// should be able to trigger route change on a pre-defined condition
if ( /* something happens */ ) {
router.triggerRouteChange( "http://test.com/booking/confirmation" );
}

// action "purchase" will be tracked on the confirmation page
router.onRouteChange( {
urls: [ "http://test.com/booking/confirmation" ],
handler: function() {
actions.send( "purchase" );
}
} );

Properties

data → {Object}

Editable property to be used for data sharing between different scripts on a page.

Code example

// script #1
router.data.template = ...;
// script #2
router.data.template["homepage"] = '...';

Configure sample campaign

Step 1

Create a site script and insert the module source code. You only need to do this once.

Map the site script to the site pages that the visitor arrives to directly. Make sure that you map the site script to the single-page URL where visitors start their journey.

As soon as the module is sent to the Homepage you can use the script by calling "router" in any script mapped to the same page.

Note: Make sure the script is above the mapped scripts that need to use it.

Step 2

Create a campaign script for a single-page app URL.

Use onRouteChange() to match secondary URLs to handler. The handler should contain the code that you would normally use on a traditional website page campaign.

Step 3

Map campaign elements / campaign scripts to the virtual page URL you established in the onRouteChange() parameters.

In this example we have created a virtual page called NG Homepage with a single mask "NG Homepage":

Extending standard functionality

How to manually trigger route change event

Single-page apps make use of other URLs to dynamically load content in two ways:

  • using HTML5 routing (navigation)
  • using direct calls to APIs

router.onChangeRoute() will be triggered automatically for navigation when a browser state change is detected.

You can manually trigger a module's route change event for direct API calls using the following syntax:

router.triggerRouteChange( targetUrl );

where targetUrl is the URL used to deliver campaign content. Go to triggerRouteChange syntax.

How to manually extend the module with custom data

In case internal single-page app data structures need to be modified as part of testing campaigns, it is recommended to pre-populate .data {Object} with references to the content structures that you may need to use later on.

This can be done with help of a single site script, so that you have the data for any campaign that requires the module. For example, you can get references to all Angular structures relevant to DOM (templates, controllers, providers, fabrics, etc).

The .data object should be accessible from any script on pages where you have the module and have extended it. Go to data syntax.