How do I use DTM for a Single Page App?

The question of how to use DTM on Single Page Apps (SPAs) is a VERY hot item right now. By Single Page App, I’m referring to a full user flow contained on a single web page, so as to provide the user a more seamless experience. Often, these pages act like typical web pages, but they don’t always change URLs or load new resources. Many common web development technologies, such as Angular.js, Ember.js, and AJAX use SPA principles.

Unfortunately, there isn’t a single great answer for how to deploy DTM- it depends on many things. I’ll work through some of the options and the limitations to be aware of.

Suppressing Page View Beacons

Whatever method you take for tracking page views in a SPA, keep in mind most SPAs do have one true “page view” when the DOM first loads. If you are going strictly with the DCR or EBR route, you may need to suppress the initial page view beacon the Analytics tool will want to set by default. Otherwise, in the example below where the developers are firing a direct call rule on all page views, you’d get TWO beacons on the first page and 1 on all subsequent pages.

Picture1

Data Layer Considerations

You’ll need to make sure that whatever the sources of your Data Elements are (CSS selector, javascript objects, cookies…) have the correct values BEFORE your rule is triggered. I have an example site showing one way you might do this for a data layer (though you’ll need to look in the source code), but ultimately it’s going to depend on your site.

Variable Persistence

One last consideration is that once your Analytics object exists (as in, the “s” in “s.pageName”), variables set on it will continue to exist unless specifically overwritten. In most cases, you’d overwrite s.pageName with a new value so it isn’t a problem, but something like s.eVar5 may be set on the first beacon in your SPA, and not desired on subsequent beacons. You can use s.clearVars() to “refresh” your “s” object, but you have to make sure it fires at the right time- for example, after the beacon on Page A, but before DTM starts mapping Data Elements to variables for the beacon on Page B. How you do this will depend on the overall deployment method you choose.

Deployment Methods

1) Direct Call Rules

Perhaps the most straight-forward approach is to have developers fire a Direct Call Rule, like _satellite.track(“page view”) on every thing YOU consider a page view, whether it’s a fresh new DOM or not.

Advantages: Disadvantages: 
  • You have ultimate control over when a page view in considered a page view.
  • If you need to clear out variables between beacons (for instance, you set s.eVar5 in the first beacon in the SPA, and don’t want it in the second beacon), Direct Call Rules don’t provide a great place to use something like s.clearsVars(). There are some potential work-arounds, but none are ideal.
  • Developers need to add more DTM-specific code (satellite.track) to your pages.
  • Direct Call Rules don’t allow for extra conditions (like “fire THIS logic on pageA, and THAT logic on pageB”) in the interface.
  • Direct Call Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

2) pushState or hashChange

Many SPA frameworks, like Angular, use a certain flag to let the browser know the user is viewing a new “page”.

pushState

DTM can listen for this flag in an Event Based Rule using a pushState or hashChange condition.

ADVANTAGES: DISADVANTAGES: 
  • No additional code is needed- most SPA frameworks are already firing something DTM can listen to
  • It’s an Event Based Rule, which allows you to fire clearVars(), and set extra conditions
  • Because you are listening for an event set by the framework, you have less control over timing. Updating a data layer BEFORE the “pushState” event is detected would be critical.
  • Event Based Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

3) Custom Event EBR

Another option, which feels a bit like a blend of the first two options, is to use a Custom Event-based Event Based Rule (and no, that’s not a typo- it’s an EBR based on the JavaScript Concept of a Custom Event). It’s possible Developers are already using this Custom Event concept for their own code and purposes, and DTM can just listen for it… or you can have developers set one specific to our DTM needs by using something like my digitalData.userAction hack.

ADVANTAGES: DISADVANTAGES: 
  • You have a little more control over timing
  • It’s an Event Based Rule, which allows you to fire clearVars(), and set extra conditions
  • May require more developer work- similar level of effort as DCRs
  • Event Based Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

 4) (NEW OPTION!) “dataelementchanged” Event Based Rule

Just in the last few weeks, a new option emerged as part of the AEM Context Hub integration. This allows DTM to listen for changes to certain data elements- for instance, you could have a rule fire whenever the “pageName” has changed. My friends at 33 sticks have a great blog post about this already for more info.

ADVANTAGES: DISADVANTAGES: 
  • You have a little more control over timing
  • It’s an Event Based Rule, which allows you to fire clearVars(), and set extra conditions
  • Requires careful consideration of when the data layer changes/loads
  • Event Based Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

16 thoughts on “How do I use DTM for a Single Page App?

  1. For the issue of not being able to do Conditional logic in a DCR, what I’ve been doing to get around that is to have an intermediary DCR. I put the logic in the “base” DCR, which will fire other DCR’s based upon the result of the logic. Not ideal, but a workable solution.

    1. Do you mean that you have a named event like “pageView” and then add your conditional logic inside of a script block, calling _satellite.track() when you want to run the specific rules? Feels like that defeats the purpose of having the DTM firing rule UI in the first place.

      1. There are some smoother ways of doing it, but essentially, yes- one of the options is to do as you describe. In the end, you can either do something like _satellite.track(“page view”) on every page, then within the code of that “page view” Direct Call Rule in DTM, add some conditions based on the current page (for instance, add something extra on search results pages)… or have conditions on your page that decide whether to fire _satellite.track(“basic page view”) vs _satellite.track(“search results”), and have a cleaner DTM interface but more rules and code to manage on the front end. Neither option is awesome, to be honest, but I do see folks doing the former much more than the latter. I believe some of the options may improve when the next major iteration of DTM comes out (probably next Spring).

  2. Hi Jennifer,

    thanks for a great post. Really helpful. I had a question whether the clearVars function would also clear the evars/props that we want to have for the session or user level in Adobe Analytics. For example, we collect user id at one stage, send it to Adobe Analytics and then on the next page view we clearVars function to remove it. Would that mean that we cannot see user id in adobe analytics and use user id to see how many pages the user visited etc.? Thanks, Jonas

    1. No, thankfully, clearVars doesn’t affect how the data is stored/persisted on the back end. If you are sending the data into an eVar and that eVar is set to not expire until, say, the end of the visit, then you could set it on page A, set clearVars, and Adobe will hold on to that value on page B. For eVars, clearVars generally doesn’t have much impact, because they’re set to persist anyways. It would just lower the “instances” metric because the eVar doesn’t get set over and over. But for props and events, clearVars will make it so your prop pageviews or your event metrics don’t get set again unless you explicitly re-set them.

    1. I’m not sure I understand the question- you can suppress the initial page load rule by putting “return false” in your tool custom code (see http://www.digitaldatatactics.com/examples/DCRsuppress.html). But if you do away with that, you need to do something else to let DTM know to fire a rule. If you want to have it fire something for “app loaded”, you could either do a Direct Call Rule or an Event Based Rule- maybe something like http://www.digitaldatatactics.com/index.php/2016/04/20/setting-up-an-event-based-rule-that-be-fired-directly-like-a-direct-call-rule/?

  3. Hi,

    Unfortunately i don’t see clearVars being used in DTM as it is very hard to use it especially when you make use of direct calls and location change (event based rule).
    Has anyone been successful implementing that function? I had to work on my own function to clear all variables based on the rule.
    I would instead expect a more friendlier option from DTM, i.e. option in the UI to confirm that you would like to clear the vars.
    Thanks

    1. I believe the UI will eventually have this option; in the meantime, if you’re in an EBR, you can add this as a custom condition for the rule:
      s.clearVars()
      return true
      This will run before the analytics tool portion of the rule, and guarantee you have a fresh slate for your variables. This is one reason I prefer EBRs over Direct Call Rules.

  4. Does anyone ever tested mutationObserver ? It does not depend on the framework and could be used to detect change in the page you are interested in.

    1. Hm, this is something I hadn’t heard of before, thanks for the tip. So this would be in place of using “dataElementChanged” (which relies on a data layer or CSS selector value, for instance), “element is present” (which doesn’t work that great, IMO), or pushstate/hash change?

  5. Hi Jenn,

    For a React based site we are using Direct call to trigger both event based & page level tracking(set to s.t() in DTM). Now we notice the variables set in the Direct call to track events are flowing into subsequent page load calls. Any suggestions to remove those additional variables?

    Thanks,

    1. Ah, the classic pesky clearVars problem. This is an issue in all SPA implementations- s.tl beacons are good about only picking up the variables you want (using s.linkTrackVars and s.linkTrackEvents) but s.t beacons will always pick up all the variables on the page. If you need to get rid of all the variables that were previously set, you can use the s.clearVars function. The problem when you are implementing your SPA through DTM is there isn’t a good place to run that s.clearVars function- you need it running after the previous beacon fires, but before you start setting variables for the current beacon.
      As I hint at in my post, this is one reason I don’t like using Direct Call Rules- there is simply no place to put that function that won’t mess with your current beacon but will consistently run at the right time. There are some workarounds- http://www.digitaldatatactics.com/examples/DCR.html has some examples but I’ll admit I hate using those workarounds, they can cause performance issues. If you can switch to using Event Based Rules instead, then you can just run s.clearVars in the custom condition code block, which will always run before the analytics tool part of your rule.
      In theory, Launch will have clearVars as an option built in to the interface (or something similar) to solve this problem. In the meantime, you may also be able to try the s.registerPostTrackCallback function that should run after your s.t/s.tl beacons. I’ll admit I haven’t done this myself, but @josh in #measure slack had posted this, which you could put in your s_code next to your doplugins:

      s.registerPostTrackCallback(function(){
      s.clearVars();
      });

      // do plugins
      s.usePlugins=true;
      s.doPlugins=function(s) {
      // stuff
      };

  6. Hi Jenn

    I have a problem , wherein I set an EBR to track click count on a link with Custom code like s.eVar6=”mylink”. Problem is this eVar6 is also gets passed in subsequent rules where this eVar6 is not even specified.
    I am just wondering if s.clearVars() would do the trick for me, but just wondering where to place it.

    1. Yes, clearVars would do the trick. You can fire it before the Analytics section of the EBR (so it doesn’t “clear” any of the variables you are TRYING to set) by putting it in the Custom Conditions code for that rule:
      clearVars
      You will need your “s” object to be globally scoped for this to work.
      Alternatively, you could use the registerPostTrackCallback function in your s_code to force variables to clear out AFTER every beacon-
      s.registerPostTrackCallback(s.clearVars)

Leave a Reply

Your email address will not be published. Required fields are marked *