Adobe Target at.js 2.0 - Part 2 - Implementation

An in depth look at how to implement at.js 2.0 to allow A/B tests to be created in the Visual Experience Composer (VEC) for use in Single Page Applications (SPA's)


Hey all, Dan here and this is the second part of my video series into at.js 2.0. The first part of this video series looked at just a general overview of at.js 2.0 – how it works and how it differs to version 1. In this part of the video series, I want to look at it from more of a technical standpoint, looking at how to actually implement it using Adobe Target and how to actually embed the trigger views function into your single page application. I have a locally hosted version of my React blog ready here which we can see over here. It currently does not have Target on it so we’ll walk through all the way from installing the extension to configuring Target to fire on initial app load and then triggering views on URL change and then custom view changes as well.

First thing to do is to make sure that the extension is configured correctly. Pretty simple, it’s just a case of going into the catalogue and then finding Adobe Target version 2 which is here and installing it. The configuration is done automatically so there’s nothing to actually do there. One key thing to take into consideration is that you will have to disable Adobe Target version 1 or version 1.71 which is what I have installed here before actually activating at.js version 2. Once that is installed, the first thing we want to do is to make sure we can get Adobe Target version 2 to fire on initial app load. That is pretty simple, it’s just a case of actually loading Target so this video is assuming that you already have app load, initial app load, event view end, and event view start set up. These are events that are emitted from your application – pretty simple to set up, there is a lot of documentation and I cover it in a previous blog so I’m not going to go into it here. So in order to get Target to fire on initial app load, if you just jump inside initial app load rule, this is just set to fire when the library is loaded. All we want to do is go to Actions, Adobe Target v2 and we want to load Target. So that will load Target library, it won’t actually fire anything which is one thing to consider (when I was looking through the documentation I missed this step and I couldn’t figure out why Target was there but it wasn’t firing!) so to get Target to fire, it’s just a case of going back into Adobe Target v2 and then fire page load request and that will fire Target on application load. There are no conditions here as we’re not actually filtering where we want this to fire, we want it to fire on any page if it’s the initial app load. So if we save this, it will build for my development environment of Adobe Launch. Once this has actually built, we will just look into the application and ensure that Target is making the appropriate requests. So if we just go over here now and open up the debugger (which can be found in the Chrome store if anyone doesn’t have it) and we give this a refresh, you’ll now be able to see that we have at.js 2.1.1 and we have now a request or a call to Target, and Target is passing in our URL which in this case is localhost.8000 but in the live application it would be which is fantastic!

So we have our application loading Target on initial load and now if we go into any other page in the application, you’ll see that there has been no subsequent request and the reason is because due to the nature of the single page application, there is no page load so the way Target has historically worked (and I’ve spoken about this in part one of this series) is that when the page reloads, Target would fire a new mbox and then pass it in certain parameters. Single page app does not do that so at.js 2.0 introduces this concept of triggering a view which we’ll look at a bit later.

But for the purposes of demonstrating what’s going on now, over in Target here I’ve created a couple of demo tests so one on the actual blog home page which is just setting the background colour to green, and one on the blog article we’re looking at which is “Implement Adobe Target server side part 4” and that also sets the background colour to orange. So what I’m going to do is set both of these live, just to demonstrate how initial app load works. So now these are live, if I go back to the actual route of the home of my blog, you’ll see now that it renders which is great because Target has loaded an initial app request on initial app load and made a request to Adobe Target and then it’s pulled back the offer and it’s applying the offer which is why you can see the green background. But obviously if we go into read, you’ll see that we do not have our orange background because the page isn’t reloading in Target so Target hasn’t made another request. But if I was to reload this page, at that point it would fire another request to Target and it would then pull this back. So as I’ve eluded to earlier, in order to get this to work seamlessly without refreshing, we need to use trigger view.

Trigger view is a pretty basic concept, it’s just telling Target that the view has changed (so something on the page has changed). If I go over here and I look at this Target flow for at.js 2.0 trigger view, I can try and demonstrate exactly what it’s doing. When the initial app loads and we load Target, it will cache all of the offers that can run on that single page application. So all trigger view does, is once the trigger view is made it goes to the cache and it will find any offers that are relevant to that view and it will then apply them. So it’s basically almost mimicking a page reload but without the reload.

How do we go about actually implementing this? If we go back to the Launch, and again we’ve got initial app load, event view end and event view start, for this we need event view start because we want Target or the trigger view in this case to fire before the view actually changes so that it doesn’t give us a flicker or anything like that. If we go in here, it’s currently empty so it’s literally a case of just going to actions, again Target v2, and this time we’re going to trigger a view. Now we want to give the view a name so in my case, I’ve got page name 2 which is the data element which I’ll show you in just one second but essentially what this is doing is just copying the path of the URL and this is what we’ll use to actually target specific URLs when we’re looking at launching a test or delivering an offer.

Then you have this option of page. Now what page does is if it’s ticked, it will count as an additional impression so if this is triggering on something that you classify as another page view, you would want to tick this page option. If this was just rendering something else into the same page (like a module at the bottom or something dynamic), you wouldn’t consider it an additional page view so you’d then untick this page section and it wouldn’t class as an additional impression. So if we save this now and we will just build (which will take just a second), while that is building I will quickly show you my data element. I will share this in the transcript (bottom of page) of this video but basically all it’s doing is taking the window.location.path so the URL, it’s stripping off any of the forward slashes or the hashes and then it is delivering it back to launch to be able to use as a data element. Now we’ll open the debugger, we’ll clear it so we can see what’s actually happening and now if I go back to my actual route to my blog, we will get a request on initial app load and we will get a request on every additional view start so every time my app emits a view start or a view change, we will get a new Target call which will pass in the data element that we have supplied to it. So in this case, it was localhost.8000 and in this case, it is implement Adobe Target server side part 4.

So now we’ve implemented the trigger view based on event starts from the application, the way we actually implement tests through the VEC will actually change. If we just go back now and we jump into the blog or the blog test which makes the background green, you’ll see we’ve got this page load event which is what we’ve been looking at previously. But if we go to browse, you’ll notice that every subsequent page now will have a view and it will say current view blog/implement-adobe-target-server-side-part-4 or if we go to a different page, blog/categories/demo. All that’s happening here is that every time this event view start is fired, it’s triggering a view and the name is what we’ve passed in through that data element which we discussed earlier. What is missing here is, when this first loads there is no view on initial application load so what we need to do is go back into initial upload and just trigger a view the way we’ve done event view start. If we add to the end, again Adobe Target v2 and we trigger a view and we just pass in data element same as before, and then we’ll save and build this. Now hopefully what we’ll see is that if we cancel out and go back, even the initial app load should have the view or a view name. So you can see now current view is blog and every subsequent page view will have a different view which is exactly what we would want. Now what we see here is the green background that is being implemented that we’ve seen on here, that is a page load event. Now that is not the way to implement tests to the VEC because it will only load if this page is the page that the user starts on. What we actually want to do is get rid of this, and actually now change the background to grey this time and you’ll see that it now sits underneath blog so what it’s saying is only render this change if the view is blog. So if we save this and go into our second test, we’ll see the page load event but we don’t want to use that so we’ll delete it and we’ll change the background here again, and now it will go underneath the view which is blog/implement-adobe-target-server-side-4 and we’ll save this.

Now if we go back and relaunch this, we see the grey background. What you’d expect to see now that we’ve implemented our views is that when we move forwards, the backgrounds change on the next page but it doesn’t and there’s a reason. It’s because of the way that in the initial Target load, it caches the potential offers so from the first URL we used which was this one here, it only caches the offers which are relevant to this page but if we go back and we look at this blog article here for example, we’ve set the URL to localhost.8000/implement-adobe-target-server-side-part-4 so it doesn’t actually match on initial app load because the URL is localhost.8000. So what you need to do for every VEC offer, is make sure that it can render on every page the user can potentially land on. The easiest way of doing this is to just take the name so now if we just add in here template rule and enter that the URL contains and the domain (and bear in mind you only need the name as you’ve still got the views to filter on so you don’t actually need to use the URL for anything like that) and we move forwards and save it. Once this syncs, when we go to the blog home page it should cache the experience for this page but it should now also cache the experience for the “Implement Adobe Target Server Side Part 4” page. So if that’s synced which it has, we will refresh this and now in theory it should let us see the grey background and then the red background which is brilliant. If we go back, we get grey and forward we get red. So this is one of those things that you just need to get into your head, that it’s only on the application load here on Adobe Target Load Target and Adobe Target Fire Page Load Request that the offer actually gets cached so you need to make sure that the offers that you generate in the VEC can be applied to any URL. So like I say the easy way (and what the documentation recommends), is just to put the name in there as the URL targeting and then use the views to get down more specific with how you actually render to certain pages as we’ve shown here.

So at this point we’ve got to an application that will load Target on load to a new page, it will then trigger views on every page and it will pass in based on the URL a page name which we can then use for views which is great. The next step to this (and what you find in single page applications) is you find content like this where it is actually dynamic so until I drop down, this content does not actually exist in the DOM (you can’t actually get to it). So there is no way of actually firing a test because if you fire it when the page loads, so say we want to make the background of this blue, if that was to fire on page load and that element isn’t there, it obviously can’t make the background blue. But we don’t have a trigger or a view in order to identify when this actually happens. There’s two ways of doing this – the first one is done through Adobe Target and is actually relatively simple as it’s just a case of going into your rules and setting up a new one (we’ll call it AuthorExpand in this case) and all you do is add a click event from the core library. So now all we have to do is find the css selector of the element which in this case is here and then paste it here, keep those changes then off the back of that, fire a trigger view (this time I’ll call it Expand-Author), we will not tick the view because we don’t want it to be an impression because it would not be something I would class as a page view and then we’ll save this. Once this is built, we can then test in Target whether that view is being fired based on a click on that element. We’ll just try that now so if we go back to blog article test, we’ll see we’ll get out a first view where the background is red and when we click here, you’ll see we now get expand-author so what we can do in there is in Compose, make the background blue and that will wait until the expand author view has been fired (i.e. wait until the element exists before firing it) so then the element will be there and the background will change to blue.

Now, this does work and it will work for quite a few use cases however if elements are completely dynamic and you don’t actually know what the selectors of them are going to be, then it becomes difficult. That’s where the third way of firing Target which is in app is the only way forward. It’s the most powerful but it’s the most development heavy so we’ll cover that off now. I’m going to use this same section here to demonstrate so what I want to do is quickly go into here and actually disable this. Now author expand is disabled, if we go back to the application and refresh, we still get our red background but we now longer get the blue background as we’ve disabled the trigger view. Now imagine this component here was completely dynamic so I would not know what the css selector was going to be or the position on the page. The way to still make trigger view work is to physically embed it in the application. It’s not difficult but it does require some developer resource. This is my React application, more specifically this is the component that renders this author section here. I have added some code to it in order to make it fire the correct trigger view now I’m not going to talk through it top to bottom, I’m going to talk through it in order of execution.

When the page loads, I have created a state or an object (whatever you want to call it), called author expand and this is blank when the application initially loads. I’ve also set a click handler on the event of expander click so when anyone clicks this, it will fire the function called handleClick. This function creates a new promise and the reason it’s in a promise is to make sure that the state is updated before the Target view is called. Without that, it’s possible that the view is called before the state is updated so it would fire the wrong view and the test would run in the wrong place. The promise makes sure that the state is updated before the Target cue fires. Inside the promise, what happens is the state of author expand is toggled between true or false and once that is done, there is a call to the function TargetView and it passes in a parameter of a string which is Author Expand plus the state of Author Expand (the object up here) which can only be true or false. So it will pass into the Target view function either author expand true or author expand false. Up here is that function which is just copied directly out of the Target documentation and it basically passes in the view name (which in my case can only be author expand true or author expand false), it’s checking if the trigger view function is available on the page and if it is, it’s then triggering a view with the specified parameter that was passed into the function.

Now as a visual demo of this, if I open up the developer tools and select my element, go to React dev tools and then I find component, we have author expand and then as it’s selected, it will turn to author expand true, author expand false. So every time this button is clicked (which is this expand handler here), then this function is called which sets the state to either true or false, and then that is passed off to Target to create a trigger view based on that name. So click it, function runs, it’s true, click it, function runs, false and we can see what’s been passed to Target right now – if we just put console.log and we’ll just pass in this which is what we pass into Target which is just author expand plus the state, now every time we expand this we can now see author expand true, author expand false. As I said, that’s exactly what’s being passed into this trigger view here and that’s what the view name will be created as in Target. So if we look at this and just go back into blog here, now when we load the page and we go to browse, when we expand this the view will either be author expand true or author expand false. In author expand true, we can now make this yellow then if we wanted, we could also do author expand false and then you could, if you wanted, do something else on that trigger as well. Now if you go forwards and put this live, we’ll quickly just refresh our application, get rid of dev tools, now we’ll get our yellow and when that collapses, this will become green so this is false. So every time the expandable is expanded, this handle click function will fire, it will change the state of author expand to true and that will fire our Target view.

That is kind of it for how you implement this within your application – it will vary massively depending on your application but the premise is the same. Somehow, you just need a trigger (which in my case is clicking on that author expand) which you then pass off into a function to supply a value (which in my case is author expand true or author expand false), and then you just pass that to the Target function. It’s basically just those three steps however depending on your application, it will differ.

There is one more thing to cover off before I conclude this video. It’s around A4T tracking, so if I open up the debugger (I’ll just clear everything here so you can see better), if I reload this and get back in our test, two requests from Target and they both have a STID ending 2018. Now the reason we have two requests here is because one of those is the page load request and the other is the trigger view request. In order for A4T to work correctly, the STID or the supplementary data ID needs to be consistent and shared between Analytics and Target. So these two end in 2018 which is correct, and this one ends in 2018 which is also correct so up to now, everything is fine. Where this falls down is when we start to navigate to other pages – if I go to demo and then implementation, I’ll create two more calls to Target because we triggered two more views and they share the same STID (2018) as the first two calls. That’s not good because Analytics has now different STIDs so Analytics is updating it’s own STID which is correct but it should be shared with what’s in Target and it isn’t. Looking at the Adobe documentation, it’s a pretty simple fix – under the section which says use trigger view to ensure A4T works correctly with at.js 2.x in single page apps, you have this bit. Visitor.getInstance returns the visitor instance we’re using in our application and normally, an STID would be updated each hit when the page reloads but because we’re in our single page application, there is no reload to do that. So this function (visitor.resetState function), it mimics the same thing much like the way trigger view mimics a page view, resetState mimics a page view to create a new STID.

To implement this is pretty simple, it’s literally just a case of copying it, going into our event view start, adding a new custom code, pasting it in and then adding the correct marketing cloud ID and then give that a save (one thing to note here is that this must be before the trigger view). Now if we save and build this, once this is built what we expect to see is on initial app load the same as what we saw before, that as we navigate through the site, hopefully we see the STID updating to mimic Analytics. Let’s give it a go by going to one of our blog pages, we’ll clear everything out, reload it and we get our same two requests which end in 93D and this ends in 93D, and if we just navigate as we did last time to demo and then implementation, we create these additional two requests which have the ending 73D and F49 on this one and then if we just check those with what we have here, and then we see our third request to Target ends in 73D and that lines up with our second request to Analytics and then this last one F49 lines up with this fourth call to Target. So that’s it – that will ensure that all the data that is collected through any tests in your single page application is sent through to Adobe Analytics correctly. That’s it for this video, thank you very much for watching and stay tuned for the next one!

Data Element

var pageName2 = window.location.pathname

if (pageName2.startsWith('#') || pageName2.startsWith('/')) {
    pageName2 = pageName2.substr(1);

if (pageName2.endsWith('#') || pageName2.endsWith('/')) {
    pageName2 = pageName2.substring(0, pageName2.length - 1);

if (window.location.href.indexOf("optisights") === -1) {
    pageName2 = "blog/" + pageName2;

if (pageName2.length == 0 || pageName2 == "blog/") {
    pageName2 = 'blog'

return pageName2