Trigger Google Analytics pageview for Angular App while using Google Tag Manager - angularjs

I am building a SPA using Angular.js. We use Google Tag Manager to load in most of our analytics/marketing scripts, which includes Google Analytics. I am also using ui-router to manage states/views.
I would like to send pageview events off to Google Analytics whenever a user browses to a different state in my app. Part of the complexity in doing this with GTM is that GTM creates a named tracker. That means that all GA events need be prepended with the tracker name. That would usually look like this:
ga('trackerName.send', 'pageview', {page: '/a/path/', title: 'A Title'});
GTM uses a randomly generated tracker name, so the tracker name needs to be grabbed at runtime. That can be done fairly simply with GA's getAll function. If you want to send the pageview event to all trackers, you would simply do:
var allTrackers = ga.getAll();
for(var i=0; i<allTrackers.length; i++) {
ga.send(allTrackers[i].getName()+".send", "pageview", {page: '/a/path', title: 'A Title'});
}
This works great for most of my pageview events. However, there is a race condition between when ui-router fires the initial view's $stateChangeSuccess (which is where I trigger the GA pageview), and when analytics.js is loaded.
Prior to analytics.js being loaded, Google Analytic's snippet creates a faux ga object, that you can send events to. This faux object does not have the rest of the ga functions on it, so you can not run getAll. Without the getAll function, I cannot get the tracker name and I cannot send pageview events.
As far as I can tell, Google Analytics does not provide any callbacks or events for when analytics.js is finished loading, so there is no way to tell when I will be able to start sending events. Right now I am using an $interval to check for the existence of ga.getAll, but that is not a very performant or ideal solution. This is what I've got:
gaCheckInterval = setInterval(function() {
if(typeof(ga) !== 'undefined' && typeof(ga.getAll) == 'function') {
clearInterval(gaCheckInterval);
sendBackloggedEvents();
}
}, 200);
Is there any other way to recognize when analytics.js has finished loading? Or any other way to send events to a named tracker, without having access to getAll?

Attempting to configure and trigger individual trackers circumvents the purpose of using a tag manager. Instead do:
dataLayer.push({event:'spa.pageView', page:..., title:...});
Where:
dataLayer is optionally renamed in the gtm snippet
spa is a handy abbreviation for your app/project/company/whatever in case you need to distinguish its actions later.
page and title can be whatever you like, you will reference them by adding dataLayer macros in your GTM container.
Then, in the tag manager you configure:
rule of {{event}} ends with pageView.
dataLayer macros for the page, title you are pushing into the dataLayer.
UA Tag (and later whatever else) to fire (1) and use the macros in (2) for the TAG parameters they override.
Repeat (3) as many times as you like for different UA properties with additional blocking rules, alternate macros or more granular firing rules as necessary.
Now you can configure the specifics and add other tag types that reuse the rules and macros without modifying the application for each change.

Related

How should I write a test to GTM tracking?

I'm building an app using NextJS/React and I'm using GTM to send track events about customers. For example, sending a track event when a customer click on the button 'Add to Cart' on the product page.
Thinking about unit tests, how should I test the tracking function that builds the payload EventData with data about the product/customer and returns window.dataLayer.push(eventData)?
I've tried to search for some article or material about writing tests to GTM tracking events but couldn't find anything.
Well push just returns true, so you probably don't want to return that. You want to build your unit tests around what you're pushing. I would typically just check what's in window.dataLayer.slice(-1).
But if you don't have access to that from your unit tests, maybe just push the eventData like you do, but then return eventData after you push it? Or maybe also hinge it on dataLayer being defined? Something like return typeof dataLayer === 'object' ? eventData : false

Is it possible to make GTM to tag some elements that are dynamically created

I'm a React developer and I barely knows how GTM works as it's not my field.
What I understand is
There are triggers on google tag manager
GTM script will find HTML elements of the triggers by defined css selectors(in my case) and tag them(put GTM attributes) when it's loaded in the page
When an event meeting the condition of a trigger occurs, the trigger will execute defined javascript(in my case)
The problem is that HTML elements possibly don't exist when GTM script is tagging because they can be created by a user interaction. And I want to tag such elements by using GTM's lifecycle method(if exists). Is there any way to do this or at least a workaround?
The best practice I can come up with is something like :
When you render the element you want to track in the React.
Let React push the datalayer about the information of the element like
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "element_render",
element_id: "#id",
element_type: "type",
any_paramter_you_need: "any_value_you_need",
})
So you can set up the Trigger in you Google Tag Manager and the type is Custom Event
Usually that should work out of the box. GTM does not tag elements directly. Instead, GTM is attached to the document root. If an element is clicked, the event bubbles up to the document root, and there GTM checks if the properties of the event target (the actually element interacted with) meet the conditions defined in the trigger.
So unless React is doing something super crazy with the document, you should be safe. Allowing to track dynamically added elements is pretty much the point of this design.
For visibility triggers, you specifically need to enable this behavior by clicking the checkbox on "observe DOM changes". There is a warning that this might impact page speed.

Trouble implementing Google Analytics gtag.js in React-based Chrome Extension

My problem in a nutshell: The window object that gtag.js operates on and the window object available in my react context (a content.js context) are different objects, and so I can't write events from my react code -- meaning I can't use analytics in my extension.
More deets:
In react <script> tags can't be loaded directly for various reasons. So I've change the documentation implementation:
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
</script>
To
export const gtag = (...args) => {
window.dataLayer.push(args)
}
export const loadAnalytics = (ga_property) => {
const script = windowdocument.createElement("script")
script.src = `https://www.googletagmanager.com/gtag/js?id=${ga_property}`
script.async = true
window.document.body.appendChild(script)
window.dataLayer = window.dataLayer || []
gtag('js', new Date())
gtag('config', ga_property, { 'transport_type': 'beacon'})
gtag('event',
'test', {
event_category: 'lookup',
event_label: 'test'
}
)
}
...
componentDidMount() {
loadAnalytics("UA-175XXXXXX-1")
}
I've come to understand through much research and gnashing of teeth that the window object in my content.js and the window object that is acted on in gtag.js once it is loaded are not the same object, and are intentionally "shadows" of each other, but still separate objects. From the documentation:
"Content scripts live in an isolated world, allowing a content script to makes changes to its JavaScript environment without conflicting with the page or additional content scripts.
Isolated worlds do not allow for content scripts, the extension, and the web page to access any variables or functions created by the others."
From what I can tell this seems to be irreconcilable without a re-write of the gtag.js source.
For reasons I still don't understand this code which references window.document
const script = window.document.createElement("script")
script.src = `https://www.googletagmanager.com/gtag/js?id=${ga_property}`
script.async = true
window.document.body.appendChild(script)
And this code in the same file which references window.document
export const gtag = (...args) => {
window.dataLayer.push(args)
}
End up pointing to two different window objects.
This post seems to reinforce that these two contexts can't communicated directly in terms of objects and functions (only messages).
For gtag.js to work in an extension, I'd need to be able to call window.dataLayer.push(...) on the window of the main web page from inside my chrome extension. And that doesn't seem possible.
Any bright ideas out there as to how to either:
Make gtag.js be loaded in the proper window.document and/or refer to the content.js context of window
or
be able to access the window object of the main page from the content.js context
Since extension code can have multiple contexts, it would be wise use the principle of separation of concerns to avoid multiple document issue altogether.
When developing extensions it is advised to run majority of your code in the background, to make use of the separate JavaScript runtime allocated for your code by the browser (and avoid slowing down the pages user is visiting or the code which appears as your extension UI). Additionally, in most cases, it is a good idea to ship the code you want to run packaged within the extension bundle. If you want to load an external resource, to your background script "document", you can use XHR and eval to execute code.
When code is executed in the background, it is available to your extension UI and content scripts using the extension and DOM messaging protocols.
First, initialize your extension in the context of your background script(s).
Then, register a message handler which will evaluate messages sent by other extension code and look for a key (usually message.type) that identifies message as carrying analytics data (usually message.payload).
Read the content of the messages that match the criteria in the handler, and use the supplied information to invoke analytics APIs.
Finally, send analytics events occurring in your UI or content scripts as messages to the background script.
This way your background script is the only place where analytics is set up, clearing up document ambiguity, your code is cleaner, because there is only one place where analytics code is accessed and the extension runs smoother because it's UI and content scripts don't need to load or know about analytics code.

Angularitics (GA) add fixed value to all tracked events

I am using Angularitics (GA analytics) in my app.
Here's what I doing in my jade file.
a.btn(href="www.some.com" analytics-on="click" analytics-label={{UserID}} analytics-category="Some Category" analytics-event="Some Event")
Now I have a ton of these and the label is always UserId so i can track events by user.
Is there a simpler way to add a constant across the board value to all events. I have an angular app and want to add the userID for the logged in user to all events i track. Looking for a way to add that info without having to add it to each element i track in the html.
I would just recommend you using GTM instead.
With Angulartics and GTM, you can access the dataLayer directly with
window.dataLayer
So you can do something like this at the start of you controller or main app.
window.dataLayer.push('userId','example123');
Then in GTM just read that into a variable and push into GA with the userId as label for the event.

How to send events in Adobe Analytics using GTM

I am using GTM to fire Adobe Analytics scripts on all the pages. It is working as desired. However I am wondering if is there a way to set up click events with no page refresh, in the same way as Google Analytics click events are set up. I am using s.tl() call however it is not sending the events to adobe omniture. Any thoughts on this. I know if I use DTM, it can be setup easily.
Assuming that you have Adobe Analytics already present on your website, s object is already present on your website. All you need to do is to put these codes on your website and fire this using GTM as a custom HTML Tag on basis of the trigger you require. There are standard events like gtm.js, gtm.dom and gtm.load already present. You can create your own custom event on top of this on basis of elements load. Using the same event you can fire these lines of codes on your website and this should give you what are you looking for.
<script type = "text/javascript">
var s = s_gi('yourreportsuiteID'); // replace this with your RSID!
s.visitorNamespace = "yournamespace" //optional
s.trackingServer = "yourtrackingserver" //optional
s.trackingServerSecure = "yoursecuretrackingserver"//optional
s.events = "event35";// your event number has to be activated in Adobe
s.linkTrackVars = "events,eVar38";//your eVar number has to be activated in Adobe
s.linkTrackEvents = "event35";
s.eVar38 = {{GTMVariable}}
s.tl( this,
"o",
"FriendlyName" );
</script>
There is nothing specific to Adobe Analytics to consider - GTM has a built-in click event handler. Go to "triggers", "new", select "click" from the options and optionally specify filters (e.g. if you want the trigger to fire only on certain pages, or only when the link has a specific class).
Assign the trigger to a custom HTML tag with your Adobe Code, save, publish and you are all set.

Resources