GTM Macro to set GUA_ID based on domain/hostname/url - google-tag-manager

We have several websites. During set-up of GTM I was wondering if I needed to set-up 1 GTM-container for alle websites (and make it variable) or set-up 1 container per website. The latter seemed like a lot of manual config.
However, to achieve a more dynamic approach I was wondering if I can set the GUA ID depending on a url/hostname regex lookup table.
Making it more difficult: We have both different domains as same domains with subfolders for languages so the lookup table has to support both.
My question: will a macro lookup like this work or does a better method exist to achieve the same result?
See the image below as example

Lookup table macro doesn't support regular expressions. You should use transitional custom JS-macro or tag in which you check your regexp's. Then you may push it to datalayer or return in macro used as a key in your lookup table
For example
v1 - js tag
<script>
categories=[
/\/moscow\/.*kupit-kvartiru\/kvartiri-studii\//,
/\/moscow\/.*kupit-kvartiru\/odnokomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/dvuhkomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/trehkomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/chetyrehkomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/bolshie-kvartiri\//
];
for (i in categories) {
if (RegExp(categories[i]).test({{url}})) {
category_type_for_retargeting=categories[i].toString().slice(1,-1);
dataLayer.push({
'event' : 'regex_event',
'category' : category_type_for_retargeting
});
}
}
</script>
v2 - js macro for lookup table
function() {
categories=[
/\/moscow\/.*kupit-kvartiru\/kvartiri-studii\//,
/\/moscow\/.*kupit-kvartiru\/odnokomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/dvuhkomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/trehkomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/chetyrehkomnatnie-kvartiri\//,
/\/moscow\/.*kupit-kvartiru\/bolshie-kvartiri\//
];
for (i in categories) {
if (RegExp(categories[i]).test({{url}})) {
category_type_for_retargeting=categories[i].toString().slice(1,-1);
return category_type_for_retargeting;
}
}
return -1; //default value
}
v3 - js macro as UA id
function() {
categories={
"UA-1234-1" : /\/moscow\/.*kupit-kvartiru\/kvartiri-studii\//,
"UA-1234-2" : /\/moscow\/.*kupit-kvartiru\/odnokomnatnie-kvartiri\//,
"UA-1234-3" : /\/moscow\/.*kupit-kvartiru\/dvuhkomnatnie-kvartiri\//,
"UA-1234-4" : /\/moscow\/.*kupit-kvartiru\/trehkomnatnie-kvartiri\//,
"UA-1234-5" : /\/moscow\/.*kupit-kvartiru\/chetyrehkomnatnie-kvartiri\//,
"UA-1234-6" : /\/moscow\/.*kupit-kvartiru\/bolshie-kvartiri\//
};
for (i in categories) {
if (RegExp(categories[i]).test({{url}})) return i;
}
}

Related

Enhanced ecommerce don't recognize internal promotion view object

I've been implementing all the enhanced eCommerce tracks for the past few weeks and I could do most of the job successfully thanks to Simo Ahava's blog. But now I'm struggling with the internal promotion view tracking.
I choose to implement the view tracking with the concept of True View Impressions also with a base on Simo's work and for products it was ok. So I modified the customTasks from the link to track internal promotion but, for some reason, the enhanced eCommerce isn't recognizing the promoView object. But it's recognizing the promoClick (?).
I've made a test: I substitute the promoClick for a impression object and it works! So, my strong guest, it's that the problem it's really on my object. My object's format can be seen here.
And to illustrate the way the object it's being constructed:
var targetElement = {{Click Element}},
event = {{Event}},
batch = window[promoBatchVariableName],
impressions = google_tag_manager[{{Container ID}}].dataLayer.get('ecommerce.promoView.promotions'),
ecomObj = { };
if (event === 'gtm.click') {
while (!targetElement.getAttribute(promoIdAttribute) && targetElement.tagName !== 'BODY') {
targetElement = targetElement.parentElement;
}
}
var latestPromoImpression = impressions.filter(function(impression) {
return impression.id === targetElement.getAttribute(promoIdAttribute);
}).shift();
var promoImpressionsArr = batch.map(function(id) {
return impressions.filter(function(impression) {
return impression.id === id;
}).shift();
});
if (event === 'gtm.elementVisibility'){
promoImpressionsArr[maxPromoBatch - 1] = latestPromoImpression;
}
console.log(ecomObj)
ecomObj.promoView = { promotions: promoImpressionsArr};
if (event === 'gtm.click') {
ecomObj.promoClick = {
promotions: [latestPromoImpression]
};
console.log("click")
}
return {
ecommerce: ecomObj
};
}
Could someone help me with some ideas?
This answer is just to close the question! As I pointed in the comments:
" I found the problem. And it's not on my object itself only. xD The problem is the undefined elements as you pointed at the beginning of our talk. I'm waiting for the dev team to change the data-attributes of the elements on our site's pages because sometimes we don't get any individual identifier variable. So, in the meantime, I've implemented a way to get always a product id even in these cases but as the identifier doesn't exist in the CSS selector if the element has an id in the 'entrance object', the element is set as undefined. "

Google Tag Manager with Json-ld - datalayer variable not returning value

I am trying to do this on ecommerce product pages. I have the following custom HTML tag firing on all pageviews of product pages. Its using datalayer varaibles, the debuging/preview tool shows the correct value for each variable:
{
“#context” : “http://schema.org”,
“#type”: “Product”,
“name”: {{productName}},
“image”: {{productImage}},
“description”: {{productDescription}},
“brand”: {
“#type”: “Thing”,
“name”: {{productBrand}}
},
“offers”: {
“#type”: “Offer”,
“priceCurrency”: “GBP”,
“price”: {{productPrice}}
}
}
I think I followed the template correctly, but this is what shows up in Google’s structured data testing tool:
“#context” : “http://schema.org”,
“#type”: “Product”,
“name”: google_tag_manager[“GTM-PHZTTZ”].macro(‘gtm1469648023100’),
“image”: google_tag_manager[“GTM-PHZTTZ”].macro(‘gtm1469648023101’),
“description”: google_tag_manager[“GTM-PHZTTZ”].macro(‘gtm1469648023102’),
“brand”: {
“#type”: “Thing”,
“name”: google_tag_manager[“GTM-PHZTTZ”].macro(‘gtm1469648023103’)},
“offers”: {
“#type”: “Offer”,
“priceCurrency”: “GBP”,
“price”: google_tag_manager[“GTM-PHZTTZ”].macro(‘gtm1469648023104’)
}
It seems to return the name of the macros from GTM’s data model rather than the value? Did I do something wrong?
You did not do anything wrong - this is an expresssion that will evaluate to the value of your datalayer variable.
google_tag_manager[“GTM-PHZTTZ”].macro(‘gtm1469648023100’) quite simply means "return the value from the variable with the internal id of 1469648023100 from the tag manager instance with the id GTM-PHZTTZ".
The Googlebot executes Javascript when it indexes the site and will be able to parse the JSON directly (and crawlers that do not execute Javascript will quite probably unable to work with Json, so that's not a concern).

How do I handle values that are dependent on the result of a helper?

So I have an Angular controller with a meteor helper method, as below.
function localeCtrl($scope, $reactive, $stateParams{
$reactive(this).attach($scope);
var self = this;
self.helpers({
locale: function(){ return Locales.findOne($stateParams.id)},
staff: function(){
// Load data from second collection based on current Locale.
// But how?
},
address: function(){
// Take self.location.address and massage it to provide
// google maps link. How?
}
tags: function(){
// Collect all unique instances of a given tag by
// iterating over the available locales.
// E. G. If 10 locales have the 'restaurant' tag, and 5
// more have the 'library' tag, I want an array of
// ['restaurant', 'library'] -- easy enough to do
// by iterating over the locales, but how do I do that
// reactively?
}
});
}
Unfortunately, I need to set additional properties based on the data fetched by locale(). I can't set them up when I initialize the controller because the value in locale() changes as data is fetched from the server. But I need access to the data in locale to, for example, create the google maps address, or fetch associated records. (They aren't imbedded in the locale document for reasons that I'm sure made sense at the time).
Edit:
Additionally, I'm using ground DB to store a local copy of the data for offline access, which makes life even more complicated.
Probably you best bet is to publish your collection using publishComposite which is implemented using the reywood:publish-composite package.
Add the package:
meteor add reywood:publish-composite
Now where you publish the Locales collection you would do something like this:
Meteor.publishComposite('locales', function() {
return {
find() {
//Put whatever you need in the query for locales
const query = {
_userId: this.userId
};
return Locales.find(query);
},
children: [{
find(locale) {
return Staff.find({ localeId: locale._id });
}
}]
};
});
Then in your controller before the helper you add this:
this.subscribe('locales');
Now you should be able to simply call the code like this:
this.helpers({
locale(){
return Locales.findOne(this.$stateParams.id);
}
});
And access it in the template like this:
locale.staff
Give that a try and let me know!

How to make a function that uses counters to compute values reactively

I'm new to meteor and I tried to use the tmeasday:publish-counts package to publish some counts. Reactivity works fine out of the box when I just read the counts, but when I use the counts in a Template.helper function, the function doesn't get updated when the counts change.
Here is what I have :
On the server :
Meteor.publish('counters', function() {
Counts.publish(this, 'searches-thisWeek', UserActions.find({
$and: [
{ action: SEARCHES_REGEX },
{ date : { $gte : moment().startOf('week').toDate()} },
{ date : { $lt : moment().toDate()} }
]
}));
Counts.publish(this, 'searches-lastWeek', UserActions.find({
$and: [
{ action: SEARCHES_REGEX },
{ date : { $gte : moment().subtract(1, 'week').startOf('week').toDate()} },
{ date : { $lt : moment().subtract(1, 'week').endOf('week').toDate()} }
]
}));
});
On the client :
Template.dashboard.helpers({
nbSearchesThisWeek : function() {
var variation = Counts.get('searches-thisWeek') - Counts.get('searches-lastWeek');
return {
currentValue : Counts.get('searches-thisWeek'),
orientation : {
sign: (variation > 0) ? '+' : '',
class: (variation > 0) ? 'up' : 'down'
},
variation : variation
};
}
});
In my template I have a :
<td>{{getPublishedCount 'searches'}}</td>
<td>{{#with nbSearchesThisWeek}}{{>variations }}{{/with}}</td>
It uses this subtemplate :
<template name="variations">
<div class="variation col-lg-12">
<span class="variationValue {{orientation.class}} col-lg-6">{{orientation.sign}}{{variation}}</span>
<span class="currentValue col-lg-6">{{currentValue}}</span>
</div>
</template>
The {{getPublishedCount 'searches'}} updates fine. It just gets my "searches" counter and updates anytime the counter changes.
However, the counters in the subtemplate execute fine at startup but never update when any of the dependent counters change.
So my question is, how and where do I make my nbSearchesThisWeek helper react to the changes on the dependent counters values ? I'm very confused when I read the documentation about Deps, Tracking, and ReactiveVars... I didn't really understand where those things should be used to make my use case work...
Thanks for your help.
Philippe
I have created a MeteorPad with code which is as far as possible the same as yours and it is all reactive, without any changes to your code.
Please note that instead of trying to recreate your UserActions collection, I have created two separate collections, UserActions and Players (the names have no significance) to create two counters just like yours. I think once you have seen the code you will agree that for the purpose of checking your code it will do.
Go to this page: http://app-cmrd2ous.meteorpad.com/ open the Chrome or Firefox console and insert the following two records and watch the counters:
UserActions.insert({name: 'bloggs', date: new Date()});
Players.insert({name: 'bloggs again', date: new Date()});
The counters are all reactive.
The meteorpad is here: http://meteorpad.com/pad/n4GwwtPesEZ9rTSuq/Dashboard
All I can suggest is that you look at where I have put the code (whether on the client or the server) - maybe that has something to do with it.
But maybe more likely is that you were running the wrong tests. In other words perhaps you got your dates wrong - you thought you were inserting a record with a date for last week, whereas actually it was two weeks ago or something like that.

How do I add or remove collection document fields server side in Meteor?

I need to add or remove fields to a doc before insert or update in the allow or deny methods. I had presumed that the transform function would provide the needed functionality.
The meteor docs state
"An optional transformation function. Documents will be passed through
this function before being returned from fetch or findOne, and before
being passed to callbacks of observe, allow, and deny."
Whenever I tried to transform and return the doc from the function either from allow or deny the transformed version of the document was not what was inserted into the mongodb. I tried transforming via 2 strategies.
Strategy 1
var ts = new Date();
return _.extend(_.pick(doc, 'name', 'discounts', 'locations', 'url_map', 'client_updated_td', '_id'), { created_td:
ts, updated_td: ts, });
Strategy 2
// Discountsroutings.fields is in /lib/Discountroutings.js
Discountsroutings.fields = ['_id', 'created_td', 'updated_td', 'client_updated_td', 'name', 'discounts', 'locations', 'url_map'];
// this is in /server/discountsroutings.js var ts = new Date();
doc.created_td = ts; doc.updated_td = ts; return _.each(doc,function(value, key, list){
if(Discountsroutings.fields.indexOf(key) == -1 ){
delete doc[key];
}
});
Neither worked. In both cases fields were not removed though fields were added.
Interestingly, I tried the same two strategies from inside an insert allow and an insert deny and only Strategy #2 worked. So, for now I am just using Strategy #2 inside Deny insert/update methods. Works fine and isn't that difficult to wire up.
Am I doing this correctly? I want to add or remove fields from a collection server side the correct way.
Steeve have you tried my collection-hooks package? Sounds like what you need
you seem to know the list of fields you want to remove. So why don't you use $set and $unset to add and remove fields?
Recently needed to do the same thing and found no example here... thought I'd share how I did it:
Using
https://atmospherejs.com/matb33/collection-hooks
http://arasatasaygin.github.io/is.js/
Prospects.before.update(function (userId, doc, fieldNames, modifier, options) {
//check existence of other segment property and make sure to delete it if segment is updated from 'Other...' to something else
if (is.existy(doc.other_segment)) {
var segment = Segments.findOne({_id: modifier.$set.segment});
if (is.not.undefined(segment) && is.not.empty(segment)) {
if (is.not.equal(segment.name, 'Other...')) {
Prospects.update( {_id: doc._id} , {$unset: { other_segment : '' } } );
}
}
}});
Hope this helps! :)

Resources