Don't Track too Much! Take Control of Your Analytics With ZK
A tutorial on how to take control of the data you're getting from your application using the ZK framework and Google Analytics.
Join the DZone community and get the full member experience.
Join For FreeForeword
Tracking and Reporting are common business requirements today. Understanding how to properly integrate and use analytics in your web application is the first step to taking better control of it.
In this article, we will discuss how the ZK framework helps take control of tracking in a ZK web app using an analytics provider such as Google Analytics. We will introduce how analytics work, and how to use it. Then, we will explore how ZK makes analytics integration easy and powerful in the context of a client/Java server web application, while still providing intentionality in our data collection instead of capturing every event indiscriminately.
All of the code shown in this article is available in this git repository.
Note: the sample code provided in the repository uses a generic Google Analytics ID to be replaced by a valid GA ID for local testing.
Hello There!
Hello friend! Sit down for a bit – we are looking at our data tracking policy today. Our ZK web application has been getting a lot of traffic recently, but our current system tracks everything or nothing. At the moment, we can either receive events for every single user click or only get a glorified page-view counter.
We need some granularity in our event log, as well as the ability to choose what to log and when to do it.
Well, Just Use the Server Connection Log Instead
It's not so easy to read, and we may need more details than that. Plus the management team needs to access the results in a user-friendly interface. I’m thinking of adding some Google analytics callbacks to our page and letting them handle the reporting console.
So, What Is Google Analytics Anyway?
Let’s call it GA for short. It’s a Google service which lets us put up tracking events in our pages. Those events are collected by Google and can be accessed through the Analytics console.
Could We Use a Different Analytics Provider?
Sure. We are using GA today because it is easy to get started and one of the most widely used options. Keep in mind that everything we do with GA today could be done with different events and analytics platforms. From our application’s point of view, the target doesn’t matter as long as we provide the right triggers.
And Can We Use GA With Our ZK Application?
Sure, ZK client pages are based on JavaScript and controlled by Java classes on the server-side. We can use direct commands from the server to send events when appropriate in our server-side logic or trigger the analytics calls from event listeners on the client-side to automatically report certain types of actions. We can even write our own Utils library to have full control over firing events from any classes on the server-side!
As a bonus, we will use ZK Executions to implement our GA integration. Since Executions are a simple mechanism used in every application, it makes it automatically compatible with every ZK architecture. This means that we will be able to deploy the same integration across multiple projects, even if they use specific patterns such as MVC or MVVM.
Alright, I’ll Bite, How Do We Start?
Well. We start by preparing our GA environment. To be perfectly honest, I just followed their getting started page.
In the end, we have an initialization script which we can use in a standard HTML page.
Got it. So, How Do I Use it in ZK?
Well. the first step would be to just add the script to a ZK page. We only need to tweak it a little bit. We are going to make the library loading script into a processing instruction, and the rest will be a standard script tag.
<?script async src="https://www.googletagmanager.com/gtag/js?id=XX-000000000-0"?>
<zk>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'XX-000000000-0');
</script>
<button label="click me!" onClick='Clients.showNotification("Hello world")'/>
</zk>
Ok, but I’m Not Going to Copy Those Scripts in Every Page, Am I?
Of course not, now we make it portable by creating a JavaScript file. There is a small obstacle though, we need to load the external gtag library before we can create the gtag object. Luckily for us, the ZK framework comes with a bundled jQuery object. We can just use the jQuery getScript()
function to retrieve the library and execute a callback after it has been loaded. It will look like this.
function afterLibraryLoaded(){
window.dataLayer = window.dataLayer || [];
window.gtag=function(){dataLayer.push(arguments);}
window.gtag('js', new Date());
window.gtag('config', 'XX-000000000-0');
}
jq.getScript("https://www.googletagmanager.com/gtag/js?id=XX-000000000-0",afterLibraryLoaded);
Great! How Do We Trigger Those Events?
Well, the most basic way is to just trigger a JavaScript call. From the gtag documentation on events, we can find this sample:
gtag('event', 'xyz');
Let’s just call that from Java with the Clients.evalJavascript()
method.
It will look like this.
Clients.evalJavaScript("gtag('event', 'Hello world button clicked');");
OK but… You Said That This Is a JavaScript library. Can’t We Call it Directly From the Client, Without the Server Round-Trip?
Sure, can do. ZK components can also receive JavaScript listeners, and those are a good place to hook up our gtag calls. First, we need to declare the “client” namespace:
<zk xmlns:w="client">
Then we can create a client listener, for example for “onClick”:
<button label="click me for client activity!" w:onClick="gtag('event', 'Hello world client button clicked');" />
That’s It?
Yup, but we can do better. For example, I don’t want to copy the same code everywhere if I can optimize it. I also don’t want to send pure JavaScript directly from our server to our clients — we can leverage the ZK framework to make it much more efficient and extensible.
I Know Where This Is Going... Extending the ZK Framework?
Absolutely. Here’s the thing: the framework already gives us access to several building blocks and automation tools. We can use those to prepare before hooks and triggers for the GA code. First, we want a small client-side object which will provide us with ready-to-fire commands. That’s just decoration around the gtag object, but it will make our life easier in the long run. I’ve made a few based on the GA documentation.
Here's a look at the zkgtag object:
zkgtag = {
afterLibraryLoaded : function() {
window.dataLayer = window.dataLayer || [];
window.gtag = function() {
dataLayer.push(arguments);
}
eventContent={};
if (zkgtag.pageTitle != "") {
eventContent.event_label = zkgtag.pageTitle;
}
if (zkgtag.pagePath != "") {
eventContent.pagePath = zkgtag.pagePath;
}
window.gtag('js', new Date());
gtag('config', zkgtag.trackingId, eventContent);
window.addEventListener('error', zkgtag.trackClientException);
},
sendEvent : function(eventAction, eventCategory, eventLabel, eventValue,
...
}
And here's how we will use it in a zul page:
<button label="click me for client activity with zkgtag object!" w:onClick="zkgtag.sendEvent('client zkgtag button clicked');" />
What if I Want to Define a Default Behavior on the Client-Side?
With ZK, you can do that too. ZK server-side components are represented on client-side by JavaScript objects called widgets. By overriding a widget class, we can embed a reporting action into any possible workflow. What makes it even more versatile in ZK is that we can register those listeners on individual instances or on whole classes of objects. We can even activate them from both Java and JavaScript code, depending on what is the easiest for each workflow.
The most powerful option is to use ZK override to add our custom behavior to every instance of a component type. For example, we can add a GA event to every "doClick" invocations on the Textbox component class with.
There are a lot of other options, and I've included some example in this page.
zk.override(zul.inp.Textbox.prototype, xTextbox ,{
doClick_ : function() {
var result = xTextbox.doClick_.apply(this, arguments);
zkgtag.sendEvent("Client-side", "User action", "Textbox click", this.getValue(), false);
return result;
}
});
Good. Now How Do We Deploy That? Add it to Every Page?
Certainly not! We are going to use another ZK feature and deploy it through lang-addon. We are just adding a JavaScript path to be loaded as part of the language definition.
Let’s Sum Up. We Have Client Triggers and a Way to Invoke Them From the Client-Side. What’s Missing?
Well, we still need some easy server-side controls. In this case, a simple Utils
class with static functions will be good. To trigger client-side actions from the server-side, we can use the existing server-to-client commands in ZK.
Alright, So Now We Write ZK Triggers for the Server to Fire Too!
Right on. We will use the zAu object (or ZK AU command object) to register automated workflows on the client-side, which we can then trigger by running commands from the server-side.
This gives a lot of versatility to our logging options, as Java now can output to our server logger (log4J or equivalent) or to the client-side to establish session profiles or give direct access to this data through the Analytics web console.
Our zAu commands can directly use our custom zkgtag
object to make it even simpler:
zAu.cmd0.sendGaEvent = function(eventAction, eventCategory, eventLabel,
eventValue, eventNonInteraction) {
zkgtag.sendEvent(eventAction, eventCategory, eventLabel, eventValue, eventNonInteraction)
};
zAu.cmd0.sendTimingSincePageLoad = function(){
zkgtag.sendTimingSincePageLoad();
};
zAu.cmd0.sendScreenView = function(screenName, appName, appId, appVersion){
zkgtag.sendScreenView(screenName, appName, appId, appVersion);
};
So Now, How Do We Fire From Them the Server-Side?
Since we have just declared some commands directly under the ZK client commands (zAu.cmd0
), we can use the ZK executions to add a command to any response. Whenever the user triggers a ZK workflow, we can also add a GA entry to be processed by the client.
The basic way to do this is to just write the command in manually such as:
Object[] data = new Object[]{"Server-side", "Test-Events", "Sent by zAu command", "my value", false};
Executions.getCurrent().addAuResponse(new AuResponse("sendGaEvent",data));
Here's a general diagram of these workflows.
Do We Really Need to Copy and Paste This Code Every Time We Send an Event?
Absolutely not. As a small cherry on top, let’s quickly make a GaUtils
class to provide static methods doing this for us.
We can simply prepare the same logic into our Util
class and call it in ZK composers or theViewModels
classes.
The Util
would just encapsulate the execution code:
public class GaUtil {
public static void sendGaEvent(String eventAction, String eventCategory, String eventLabel, String eventValue, boolean eventNonInteraction){
Object[] data = new Object[]{eventAction, eventCategory, eventLabel,
eventValue, eventNonInteraction};
Executions.getCurrent().addAuResponse(new AuResponse("sendGaEvent",data));
}
...
}
And here's how we would use it:
GaUtil.sendGaEvent("Server-side", "Test-Events", "Sent by GA Util","my value",false);
Now, How Do We Control Exactly What We Send to Our Analytics Provider?
First, we really have to narrow down the event we need to track, as well as the data that we want to retrieve. In the context of the Java classes, ZK provides an easy answer. Since tracking happens "on demand," when we invoke our Util
to send a tracking event to the client, we are already in full control of the timing and the content.
On the client-side, we have to be more careful not to choose a scope larger than what we need. For example, if we only need to retrieve events when the user selects a specific list entry (listitem
), we should either register a listener on that entry or override it. It wouldn't make sense to override the whole Listitem
class, as this would cause events to be sent by every single Listitem
in our page.
On the other hand, it doesn't help to register the same listener on every single Listitem
if we require a tracking point for each selection.
As with many things, a careful analysis of our tracking needs is the first step in a successful data tracking policy, as it will inform every choice down the line.
In Conclusion
We 've seen how ZK can be easily extended to integrate a client-side analytics library, and how to control which data is collected by this library and when to send it.
Opinions expressed by DZone contributors are their own.
Trending
-
Integration Testing Tutorial: A Comprehensive Guide With Examples And Best Practices
-
Integration Architecture Guiding Principles, A Reference
-
Does the OCP Exam Still Make Sense?
-
A Data-Driven Approach to Application Modernization
Comments