GA4 Event & Parameter naming - google-analytics

I have two processes for our website: opt-in & opt-out.
We track the customer button clicks going into each process along with a cancel & confirm button that follow.
I have configured the GA4 events in GTM to fire on their respective buttons as follows:
Opt In:
Enter Button: Event Name: opt_in, Parameter Name: entry
Cancel Button: Event Name: opt_in, Parameter Name: cancel
Confirm Button: Event Name: opt_in, Parameter Name: confirm
Opt Out:
Enter Button: Event Name: opt_out, Parameter Name: entry
Cancel Button: Event Name: opt_out, Parameter Name: cancel
Confirm Button: Event Name: opt_out, Parameter Name: confirm
From my brief understanding of GA4, in order to report on these I need to have custom dimensions configured.
So I create the following custom dimensions:
Dimension Name: opt_in_entry, Event Parameter: entry
Dimension Name: opt_in_cancel, Event Parameter: cancel
Dimension Name: opt_in_confirm, Event Parameter: confirm
All OK.
When I add in the opt_out parameter is complains with the following error:
There is already a dimension or metric registered with this parameter
name.
Do I need to make my event parameter names unique or should I make my event names unique and ditch the parameters?

Your mistake is binding EPs (Event Parameters) to ENs (Event Names). They're somewhat independent dimensions.
So that you would understand, the limit GA4 sets on the number of distinct event names is about 500. While the limit for the number of distinct custom dimensions is only 50. There is no expectation whatsoever that you would make custom dimensions for specific events. That said, you want your custom dimensions and event properties quite vaguely named.
It is normal to have the same event property and its custom dimension to mean different things depending on event.
In your case, however, they mean the same thing. All you need to do is to name them properly. Instead of having:
Dimension Name: opt_in_entry, Event Parameter: entry
Dimension Name: opt_in_cancel, Event Parameter: cancel
Dimension Name: opt_in_confirm, Event Parameter: confirm
You could have
Dimension Name: entry, Event Parameter: entry
Dimension Name: cancel, Event Parameter: cancel
Dimension Name: confirm, Event Parameter: confirm
It's pretty common and straightforward to name your CDs just as you name your EPs. Much easier to debug and maintain them that way.
However, why are you really wasting three dimensions when you can really just put all of this in one dimension, say calling it CTA or even just type so that it could be reused with other events.
So now your tracking network requests would look like:
...
en: "opt-in",
ep.type: "entry",
...
ep.type can be whatever type you want to set there.
And in GA4, in the explorer, you would just pull two events: the opt-in and the opt-out and then just one type dimension for both events. You could also use a segment to limit the type values to these two events since pulling two event names can be awkward given how explorer is still has a long way to go in terms of usability.
This setup is quite elegant and doesn't waste space needlessly, while still allowing every kind of analysis.
Later on, you could reuse the type property/dimension with, let's say, a scroll event. Where you would store in the type variable whether the scroll was done with mouse, or nav bar. If that's valuable to know.
Or you could decide to track the add to cart clicks on product pages, and type could store whether the main CTA had been clicked, or the bottom add to cart CTA, or maybe something else.
This basic custom dimensions recycling is intended and it's a must for proper analytics tracking maintenance. Once you have more custom dimensions and events permutations than you can easily keep track of, it's time to document them and that documentation would be a part of your analytics SDR (Solution Design Reference). Larger and more complex implementations would definitely always have a document like that to make it easier to manage.

Related

Asynchronously maintaining state integrity in Redux

Suppose I have a Redux store. Its state has the userCoordinates field
with the user's current coordinates:
{
userCoordinates: [3.1457, 2.1728],
...
}
Then suppose the store has 20 more fields with data that depends on the
coordinates:
{
...
nearbyFriends: ["Bob", "Jimmy"],
nearbyShops: ["Point & Click tools", "Bobby Ray's"],
address: "42 Enth Street, Townsville, Countryland",
weather: "Cloudy",
...
}
Values in all of these fields are loaded over HTTP from
various services, one separate request for each field.
Now suppose the user's geolocation is updated, and an action is dispatched that
changes the value of userCoordinates:
dispatch(updateCoords([1.6180, 1.4142]));
Now userCoordinates has new coordinates and all 20 other fields have invalid data.
If the goal is just to keep all the fields "eventually" valid, where do I make the requests to fetch the new data?
How the method changes if I use React-Redux and there are lots of components using many different combinations of these fields, with some fields used by more than one component and some fields not used at all?
Depending on how this is architected, I would do one of two things.
1: Compare states in your reducer. If there is a change in the user coordinates in the state, you could add something to the store like updateNeeded: true, then have a check wherever you are loading your other data so that if updateNeeded is true, you fire an ajax call to go get new data from the server.
2: If it is set up the right way, you could used componentWillReceiveProps to compare the props. If there is a change in userCoordinates, dispatch an action to go get the other data fields.
Of course, this would just be easier if you could just get all the data with one call...

Getting Event data within a CustomView after setRange is called

So I'm trying to build my own custom view for a month's worth of data (based on the "agenda-views.html' example) and I've been able to create a custom View class which adds itself as a button on the list of .. buttons.
When I click on that button (MyView), its setRange is called so I know the range of Events to display.
The View's renderEvents is called but is passed all of the Events that the Calendar knows about, so I have a two parter question:
(a) Is there a way I can use existing code to do the equivalent of: "given this range, give me all of the Events fullCalendar knows about"?
- or -
(b) Do I use an XHR here to pull back the data for the given range (now that I know what it is is) and Render it
I'd rather do (a) as it's more efficient (less requests etc) but I'm finding myself swamped in code and, after reading through quite a lot it haven't really found a method that says 'Get all events for this range'.
Alternatively, am I missing something? How does renderEvents know what the current range is and then render them? Or, should I be using renderSelection, which doesn't seem to get called.
Thanks!
Set up your events as a json feed, when the URL is called it will pass the start and end dates for the current view. In your function/method/action you then retrieve your events for the given dates and return them.

Call child form programmatically with parameter / filter

I'm creating a customization where on a click of a button, I need to allocate a charge for a particular purchase order / invoice journal.
From the front end, I would accomplish this by following the purchase order life-cycle and invoicing it. I would then go under the invoice tab of the PO, click Invoice Journals -> Charges -> Adjustment . This will open up my desired form where I will select a charges code, charges value, currency and category, and then I will click 'Ok' and have the system take care of the rest of the process.
Form name: MarkupAllocation_VendInvoiceTrans
Parent form Name: VendInvoiceJournal
You can see that the child form gets called with a few parameters such as the invoice number, there obviously needs to be that link. If I go into the AOT under forms, I right click and open up VendInvoiceJournal, but I wouldn't be able to open up MarkupAllocation_VendInvoiceTrans because it requires parameters.
Objective:
A: To open MarkupAllocation_VendInvoiceTrans through code where I manually pass those parameters to link to the parent table. I would provide the invoice number and such. The objective is to skip opening the parent table and manually going into the adjustments. I want to open that form directly and have it link to whichever record I specify.
B: I need to be able to pass a _ChargesValue parameter and have that be pre-populated for me. I don't know if this is possible, so I wanted to ask and confer. Ideally, I should be able to click a button on my custom form, and have MarkupAllocation_VendInvoiceTrans form directly open for a specified invoice, with pre-populated values on the line.
I know I should be tackling this problem one step at a time, so step A is priority number one.
I can open up the parent form with relative ease like so, but I cannot do the same for the child form. Obviously the same time of approach won't work, as I need to specify the relationship of the parent table before I open it.
private void allocateMarkup()
{
Object formRun;
Args args = new Args();
VendInvoiceJour jourTable;
;
select * from jourTable where jourTable.PurchId == 'PO000001191';
args.name(formstr(VendInvoiceJournal));
args.record(jourTable);
formRun = ClassFactory.formRunClass(args);
formRun.init();
formRun.run();
formRun.wait();
}
How would I be able to do so?
(Side note, I realize this whole form calling could be avoided if do all the transactions programmatically instead of letting the out of the box functionality handle it, but the markup and allocation logic is a beast of it's own and to me seems much more complicated than doing this. If someone has done it this manual way, any help on that would be greatly appreciated as well)
If I read your post right, you just want to open the Charges>Adjustment for a certain invoice. Here is one simple method:
MarkupAdjustment markupAdjustment = new MarkupAdjustment();
markupAdjustment.vendInvoiceJour(VendInvoiceJour::findFromPurchId('PO 120079'));
markupAdjustment.run();

Event tracking empty label string

I have a function which handles tracking of a certian event, like so:
var trackAddress = function (providedProduct, searchedProduct) {
_trackEvent('Address found', providedProduct, searchedProduct);
}
Now what will happen if searchedProduct is undefined or an empty string?
The thing is, in Google Analytics I can see that the sum of all event actions is equal to the total number of events. That is not the case in event labels.
What could be the cause for this?
I'm sure you know this, but for the sake of argument this is the anatomy of an event tracker:
_trackEvent(category, action, opt_label, opt_value, opt_noninteraction)
category (required): The name you supply for the group of objects you want to track.
action (required): A string that is uniquely paired with each category, and commonly used to define the type of user interaction for the web object.
label (optional): An optional string to provide additional dimensions to the event data.
value (optional): An integer that you can use to provide numerical data about the user event.
non-interaction (optional): A boolean that when set to true, indicates that the event hit will not be used in bounce-rate calculation.
Now in case a required parameter is missing (like action in your case) there's must be a mechanism within Google Analytics that will invalidate the event altogether. Conversely, an optional parameter, won't affect the event tracking but rather the report. To sum up, the result is the same: Loss of data.
A possible way around this to provide default parameters for your function arguments like so:
providedProduct = typeof a !== 'undefined' ? providedProduct : "defaultValue";
Further Reading: Setting Up Event Tracking

Lightswitch HTML databinding to a details collection

I have a simple master/details relationship where one order can have multiple revenue allocations. The order has a collection that contains these.
I want to sum a property in my revenue allocation objects and ensure that it adds up to my order total. However, if I databind on the count property of the allocations collection this gets called when you first add an empty object and not when that object has been populated. So an empty allocation is added at the time the "Add allocation" screen is created and the databind function called. That of course means that when the save button on the "Add allocation" screen is clicked, the databind function isn't called again.
Anyone got any ideas? I basically want my databind function to be called when the save button is clicked in the "add screen" and not before.
This is the HTML client - NOT Silverlight
I'm pretty sure that the solution would be to use an OData query to get your aggregate data within the databinding function of the save button - or perhaps a separate button (e.g. "Tally Order Totals"). Exactly how you do that? A bit too hard for me to answer right now, but start with a new button TallyOrderTotals and a new data field for your total. Edit the post_render event for TallyOrderTotals and lookup the allocations in the javascript in which you data bind the value of the new data field.
Somewhere you will need a piece of code that looks something like this:
myapp.activeDataWorkSpace.<datasource>.RevenueAllocations
.filter("OrderID eq " + msls._toODataString(<orderID>, ":String"))
.execute()
.then(function (result) {
// assign the result somewhere
}
I'm not saying that's something you can cut-and-paste - but definitely look at the msls.js documentation and see what you can do with querying your data inside the event context.
One quick point however - if you only need to calculate that total as a verification step, consider doing it in the SaveExecuting() event on the server side. This will allow you to throw an exception back up the tree to your HTML page which the msls.js script should render on the client side.
Hope that helps. :)

Resources