Adobe Launch’s Rule Ordering is a big deal for Single Page Apps

In November, I posted about some of the ways that Launch will make it easier to implement on Single Page Apps (SPAs), but I hinted that a few things were still lacking.
In mid-January, the Launch team announced a feature I’ve been eagerly awaiting: the ability to order your rules. With this ability, we finally have a clean and easy way to implement Adobe Analytics on a Single Page App.

The historical problem

As I mentioned in my previous post, one of the key problems we’ve seen in the past was that Event-Based Rules (EBRs) and Direct Call Rules (DCRs) can’t “stack”. Let me explain what I mean by that.

Not a single page app? Rule Stacking rocks!

For example, let’s say I have an internal search “null results” page, where the beacon that fires should include:

  • Global Variables, like “s.server should always be set to document.hostname”
  • Variables specific to the e-commerce/product side of my site with a common data layer structure (pageName should always be set to %Content ID: Page Name%)
  • Search Results variables (like my props/eVars for Search Term and Number of Search Results, and a custom event for Internal Searches)
  • Search Results when a filter is applied (like a listVar for Filter Applied and an event for User applied Search Filter)
  • Null Results Variables (another event for Null Internal Searches and a bit of logic to rewrite my Number of Search Results variable from “0” to “zero” (because searching in the reports for “0” would show me 10, 20, 30… whereas “zero” could easily show me my null results)

With a non-SPA, when a new page load loads, DTM would run through all of my page load rules and see which had conditions that were matched by the current page. It would then set the variables from those rules, then AFTER all the rules were checked and variables were set, DTM would send the beacon, happily combining variables from potentially many rules.

Would become this beacon:

If you have a Page Load Rule-based implementation, this allows you to define your rules by their scope, and can really use the power of DTM to only apply code/logic when needed.

Single Page App? Not so much.

However, if I were in a Single Page App, I’d either be using a Direct Call Rule or an Event-Based Rule to determine a new page was viewed and fire a beacon. DCRs and EBRs have a 1:1 ratio with beacons fired- if a rule’s conditions were met, it would fire a beacon. So I would need to figure out a way to have my global variables fire on every beacon, and set site-section-specific and user-action-specific variables, for every user action tracked. This would either mean having a lot of DCRs and EBRs for all the possible combos of variables (meaning a lot of repeat effort in setting rules, and repeated code weight in the DTM library), or a single massive rule with a lot of custom code to figure out which user-action-specific variables to set:

Or leaving the Adobe Analytics tool interface altogether, and doing odd things in Third Party Tag blocks. I’ve seen it done, and it makes sad pandas sad.

The Answer: Launch

Launch does two important things that solve this:

  1. Rules that set Adobe Analytics Variables do not necessarily have to fire a beacon. I can tell my rule to just set variables, to fire a beacon, or to clear variables, or any combination of those options.
  2. I can now order my rules to be sure that the rule that fires my beacon goes AFTER all the rules that set my variables.

So I set up my 5 rules, same as before. All of my rules have differing conditions, and use two similar triggers: one set to fire on Page Bottom (if the user just navigated to my site or refreshes a page, loading a fresh new DOM) and one on Data Element Changed (for Single Page App “virtual page views”, looking at when the Page Name is updated in the Data Layer).

UPDATE: I realize now that you probably wouldn’t want to combine “Page Bottom” and “Data Element Changed” this way, because odds are, it’s going to count your initial setting of the pageName data element as a change, and then double-fire on page load. Either way, it’s less than ideal to use “data element changed” as a trigger because it’s not as reliable. But since this post is already written and has images to go with it, I’ll leave it, and we can pretend that for some reason you wouldn’t be updating your pageName data element when the page initially loads. 

When I create those triggers, I can assign a number for that trigger’s Order:


One rule, my global rule, has those triggers set to fire at “50” (which is the default number, right in the middle of the range it is recommended that I use, 1-100). The rule with this trigger not only sets my global variables, it also fires my beacon then clears my variables:

Most of my other rules, I give an Order number of “25” (again, fairly arbitrary, but it gives me flexibility to have other rules fire before or after as needed). One rule, my “Internal Search: Null Results” rule is set to the Order number “30”, because I want it to come AFTER the “Internal Search: Search Results” rule, since it needs to overwrite my Number of Search Results variable from “0” (which it got from the data layer) to “Zero”.

This gives me a chance to set all the variables in my custom rules, and have my beacon and clearVars fire at the end in my global rule (the rule’s Order number is in the black circles):

I of course will need to be very careful about using my Order numbers consistently- I’m already thinking about how to fit this into existing documentation, like my SDR.

Conclusion

This doesn’t just impact Single Page Apps- even a traditional Page Load Rule implementation sometimes needs to make sure one rule fires after another, perhaps to overwrite the variables of another, or to check a variable another rule set (maybe I’m hard coding s.channel in one rule, and based on that value, want to fire another rule). I can even think of cases where this would be helpful for third party tags. This is a really powerful feature that should give a lot more control and flexibility to your tag management implementation.

Let me know if you think of new advantages, use cases, or potential “gotchas” for this feature!

Adobe DTM Launch: Improvements for Single Page Apps

For those following the new release of Adobe’s DTM, known as Launch, I have a new blog post up at the Cognetik blog, cross-posted below:

It’s finally here! Adobe released the newest version of DTM, known as “Launch”. There are already some great resources out there going over some of the new features (presumably including plenty of “Launchey Launch” puns), which includes:

  • Extensions/Integrations
  • Better Environment Controls/Publishing Flow
  • New, Streamlined Interface

But there is one thing I’ve been far more excited about than any other: Single Page App compatibility. I’ve mentioned on my personal blog some of the problems the old DTM has had with Single Page Apps:

  • Page Load Rules (PLRs) can’t fire later than DOMready
  • Event-Based Rules (EBRs) and Direct Call Rules (DCRs) can’t “stack” (unlike PLRs, there’s a 1:1 ratio between rules and analytics beacons, so you can’t have one rule set your global variables, and another set section-specific variable, and another set page-specific variables, and have them all wrap into a single beacon)
  • It can be difficult to fire s.clearVars at the right place (and impossible without some interesting workarounds)
  • Firing a “Virtual Page Load” EBR at the right time (after your data layer has updated, for instance) can be tricky.

So much of this is solved with the release of DTM Launch.

  • You can have one rule that fires EITHER on domReady OR on a trigger (Event-based or Direct Call).
  • You have a way to fire clearVars.
  • You can add conditions/exclusions to Direct Call rules

There are other changes coming that will improve things even further, but for now, these changes are pretty significant for Single Page apps.

Multiple Triggers on a Single Rule

If I have a Single Page App, I’ll want to track when the user first views a page, the same as for a “traditional” non-App page. So if I’m setting EBRs or DCRs for my “Virtual Page Views”, I’d need to account for this “Traditional Page Load” page view for the user’s initial entry to my app.
In the past, I’d either have a Page Load Rule do this (if I could be sure my Event-Based Rules wouldn’t also run when the page first loaded), or I could do all my tracking with Event-Based Rules, and I’d have to suppress that initial page view beacon. I may end up with an identical set of rules- one for when my page truly loads, and one for “Virtual Page Views”.

Now, I can do this in a single rule:

Where my “Core- Page Bottom” event fires when the page first loads (like an old Page Load Rule):

…and another “Page Name Changed” event that fires when my “page name” Data Element changes (like an old Event-Based Rule):

No more need to keep separate sets of rules for Page Load Rules and Virtual page views!

Clearing variables with s.clearVars()

Anyone who has worked on a Single Page App, or on any Adobe Analytics implementation with multiple s.t() beacons on a single DOM, has felt the pain of variables carrying over from beacon to beacon. Once an “s” variable (like s.prop1) exists on the page, it will hang around and be picked up by any subsequent page view beacon on that page.

Page 1

Page 2

Page 3

Page 4

s.pageName

Landing

Search Results

PDP > Red Wug

Product List

s.events

(blank)

event14

prodView

prodView

s.eVar1 (search term)

(blank)

Red Wug

Red Wug

Red Wug

My pageName variable is fine because I’m overwriting it on each page, but my Search Term eVar value is hanging around past my Search Results page! And on pages where I don’t write a new events string, the most recent event hangs around!

In the old DTM, I had a few options for solving this. I could do some bizarre things to daisy-chain DCRs to make sure I could get the right order of setting variables, firing beacons, then clearing variables. Or, I could use a hack in the “Custom Code” conditions of an Event-Based Rule, to ensure s.clearVars would run before I started setting beacons. Or, more recently, I could use s.registerPostTrackCallback to run the s.clearVars function after the s_code detected an s.t function was called.

Now, it’s as simple as specifying that my rule should set my variables, then send the beacon, then clear my variables:

Directly in the rule- no extra rules, no custom code, no workarounds!

Rule Conditions on ALL Rule Types (including Direct Call)

If I were using Direct Call Rules for my SPA, in the past, I’d have to account for Direct Call Rules having a 1:1 relationship with their trigger. If I had some logic I needed to fire on Search Results pages, and other logic to fire on Purchase Confirmation pages, I could have my developers fire a different “_satellite.track” function on every page:

Then in each of those rules, I’d maintain all my global variables as well as any logic specific to that beacon. This could be difficult to maintain and introduces extra work and many possible points of failure for developers.

Or, I could have my developers fire a global _satellite.track(“page view”) on every page, and in that one rule, maintain a ridiculous amount of custom code like this:

This would take me entirely out of the DTM interface, and make some very code-heavy rules (not ideal for end-user page performance, or for DTM user experience — here’s hoping your developer leaves nice script comments!)

Now, I can still have my developers set a single _satellite.track(“page view”) (or similar), and set a myriad of rules in Launch, each using that same “page view” trigger, but each with a condition so you can set different variables in different rules directly in the interface when your developers fire _satellite.track(“page view”) on your Search Results versus when they fire _satellite.track(“page view”) on your Purchase Confirmation page:

I’d love to say all my SPA woes were solved with this release, but to show I haven’t entirely drunk the Kool-aid, I will admit some of my most wished-for features (and extensions) aren’t in this first release of Launch. I know they’re coming, though- future releases of Launch will add additional features that will make implementing on a Single Page App even simpler, but for now, it still feels like Christmas came early this year.

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.