📜 ⬆️ ⬇️

Custom templates in GTM: figure it out

At the end of May, Google introduced a new feature in Google Tag Manager (GTM): Custom Templates or custom templates. Let's see why it is needed, how to use it, what are the differences from HTML tags and JavaScript variables.

As an example, we will consider the creation of a Custom Template for a pixel of dynamic retargeting “VKontakte” and further customization of GTM tags through it.



Simple words about Custom Templates
Creating a Custom Template
• Info tab
• Fields tab
• Code tab
• Permissions tab
Customize and test the tag according to the created Custom Template
• Pageview
• AddToCart
• Test working out
Conclusion
')

Simple words about Custom Templates


Custom Templates are templates for which users can create new tags or variables. GTM has ready-made templates (in the Featured or Featured section), for example, Google Analytics tag, Google Optimize, and others. Now we can add them with our own templates. After creation, they will appear in the Custom tab:



The key difference from HTML tags and JS variables is that when a user creates a tag or a variable using a predefined template, it does not interact with the JS code.

The code of the user template is written by the developer or web analyst at the stage of its creation. Then, when creating a tag or variable using a custom template, all interaction takes place through the interface (which is also configured when creating a custom template):



Accordingly, compared to writing an HTML tag or a JS variable, it becomes an order of magnitude easier to set up a tag or variable using a custom template, since it does not require knowledge and skills of working with JavaScript.

Another big plus of custom templates is that the probability of “putting” a site decreases due to an error in the JS code of the tag.

In our example, to set up the tag of dynamic retargeting “VKontakte” you no longer need to contact the developers - everything can be set up independently, even the transfer of data about the goods (with Google’s advanced e-commerce site configured), having only GTM experience.

Creating a Custom Template


Since we are creating a tag template, we need to go to the Templates section and click the New button in the Tag Templates section.



After this, the Template Editor opens:



In the left part of the editor there is a settings window, in the right part there is a preview window and a console. In the settings window there are four tabs that are needed to create and work with the template.

Info tab


This tab contains information about the tag: name, description, icon. This is the information that we will see when creating a new tag in the list of templates:



There are requirements for the tag icon: PNG, JPEG or GIF format, resolution at least 64x64 pixels and size no more than 50 KB.

Fields tab


This section creates our template interface. The interface consists of various fields and forms with which the user interacts at the stage of creating a new tag or variable.

Further, the information that the user entered when creating the tag using the interface is used in the template code.

To add a new item, click the Add Field button. The item type selection window appears:



GTM allows you to select the following types of interface elements:


After adding an element, you must give it a clear name, which will then be used in the code. This is a kind of variable name - it should be clear and reveal the essence of the created interface element. For example, the name “ID” does not mean anything concrete, but the name “pixelIDs” already shows that this element stores the IDs of the pixels that the user entered:



Next, you need to go to the settings of each element and activate the necessary properties.
In different situations, different properties of the interface element are required, so by default they are all hidden and we need to activate exactly those that are needed now:



Different types of elements have different properties, I will indicate the most frequently used, which almost all elements have:

1. Display name . This is the name that the user will see in the interface when creating the tag:



2. Example value . This is a hint to the user about what values ​​to enter in the field:



3. Help text . This is the text that the user will see if the cursor hovers over the element's help icon:



4. Rules for data verification . In this property, you can set certain rules for checking data entered by the user in the field, and, if necessary, the error text that appears when you try to save a tag with data that did not pass the test.

For example, you can specify that the field must be filled. The second example: you need to get an email address from the user, then you can make sure that the data entered by the user must match the regular expression . * @. * \ .. * .



To specify the error text that appears if the entered data does not comply with the validation rules, you need to activate the advanced settings of the rule:





Also in the advanced settings you can specify the conditions under which this rule is activated (the Enable Condition field).

5. Terms of inclusion . These are the conditions under which the user will have this interface element.

For example, in order not to overload the template with interface elements, you can make the necessary elements appear when the checkbox is selected. That is, suppose if a user wants to set up product data transfer to a pixel (this is possible when Google’s extended e-commerce site is configured), then he sets the “Use dataLayer to transfer product data” checkbox and after the checkbox is selected, elements appear in the tag interface required to configure such a transfer. If the checkbox is not checked, then there are no elements in the interface.



I note that here it is necessary to specify the name of the element, which was necessary to assign to it immediately after the addition.

As settings are added and the interface is created, all changes can be immediately viewed and tested in the preview window:



Code Tab


This tab is a code editor.

The GTM Custom Templates code is written in “trimmed” JavaScript ES6 and runs in an isolated environment, where all communication with global data (that is, directly with the page) occurs via the API. There are no global objects, such as window or document, in it, respectively, the usual methods either. For example, constructors (new Object and similar), setTimeout, parseInt, delete, etc. - all this will not work in the Custom Template.

But for all this there is an API. And, therefore, the writing of code for the Custom Template must begin with the fact that we define the APIs that we will use in our code:

// API //     const copyFromWindow = require('copyFromWindow'); //     const setInWindow = require('setInWindow'); //      const injectScript = require('injectScript'); //    const callInWindow = require('callInWindow'); //  ,     ,    const makeTableMap = require('makeTableMap'); //   URL  const getUrl = require('getUrl'); //    const getQueryParameters = require('getQueryParameters'); //     const makeInteger = require('makeInteger'); //    const makeString = require('makeString'); //  setTimeout const callLater = require('callLater'); //  console.log const logToConsole = require('logToConsole'); 

A full list of APIs with detailed documentation is available in the Google Help For Developers .

I will show you with examples how to work with the API:
Action descriptionClassic JSCustom Template API
Console outputconsole.log ('Hi');logToConsole ('Hi');
Set timersetTimeout (function, 100);callLater (function);
Conversion to stringString (1234);makeString (1234);
Convert to integerparseInt ('1234', 10);makeInteger ('1234');
Page hostwindow.location.hostnamegetUrl ('host');

As can be seen from the table of examples, after defining the API, it should be used instead of standard JS constructs.

After we have defined the API, it is desirable to define an object with the settings that the user entered. This can be done after, for example, during executable code, requesting the necessary data from the user's settings. But if we define the settings object from the very beginning, then working with the code becomes simpler and clearer, since all user settings are stored in a separate object.

To get data from an interface element, you need to use the data construct.

{{item name}}:

 //    const settings = { //  event: data.event, //ID  () pixelIDs: data.pixelIDs, //ID - (  1 -) priceListId: data.priceListId, //   -? fewPriceLists: data.fewPriceLists, //ID - (    ) priceListIds: data.priceListIds === undefined ? data.priceListIds : makeTableMap(data.priceListIds,'hostname','priceListId'), //  ecommerce   ? ecommerceUse: data.ecommerceUse, // ecommerce   eventEcommerce: data.eventEcommerce, //     siteSearchQueryParam: data.siteSearchQueryParam }; 

Note: if you pass undefined to the makeTableMap method, this will cause a script error, so I use the construction with the ternary operator (abbreviated if-else construction entry) to filter such scripts.

About the makeTableMap method.
If the interface uses an extended table, then the data in it is stored in this form:

 [ 'key': 'k1', 'value': 'v1'}, 'key': 'k2', 'value': 'v2'} ] 

After being processed by the makeTableMap method, the data becomes a regular object with key-value pairs:

 { 'k1': 'v1', 'k2': 'v2' } 


Another requirement for the Custom Template code: in case of successful execution of the tag, you must call the data.gtmOnSuccess () method, and in case of an error, the data.gtmOnFailure () method.

For example, in my code, the data.gtmOnSuccess () method is called after the successful sending of the request, and the data.gtmOnFailure () method - in the case of unsuccessful loading on the page of the external script VK openapi.js.

After defining an API and defining an object with settings, you can start writing a pixel refinement algorithm.

The main thing to remember is the following:

• If you need to get a global variable - use the API method copyFromWindow.

 copyFromWindow('VK'); //VK -   ,     

• If you need to set a global variable - use the API method setInWindow.

 setInWindow('openapiInject', 1); //openapiInject -   ,    //1 - ,    . 

• If you need to run a global function, use the callInWindow API method.

 callInWindow('VK.Retargeting.Init', p); //VK.Retargeting.Init -   ,    //p - ,      

• If you need to add an external script to the page - use the API injectScript method.

 injectScript('https://vk.com/js/api/openapi.js?159', pixel.setVkAsyncInit(), data.gtmOnFailure, 'vkPixel'); //https://vk.com/js/api/openapi.js?159 -  ,      //pixel.setVkAsyncInit() - ,        //data.gtmOnFailure - ,        //vkPixel -  ,  ,   URL  .    ,   JavaScript       

• If you need to get the URL (or part of it) - use the API getUrl method.

 getUrl('host'); //host -  URL,  .  ,  host: protocol, port, path, extension, fragment, query. 

As I wrote above, the Custom Template supports JS ES6. It is desirable to use this syntax, as it reduces the code and makes it more readable, and the work of JS - more predictable and similar to other programming languages.

More on JS ES6 syntax
The main thing that is desirable to use is the arrow functions and the declaration of the variables const and let instead of var.

A variable declared via const is a constant whose value cannot be changed.

A variable declared via let differs from a variable declared through var as follows:

  • let is not added to the global window object;
  • the visibility of the let variable is limited to the declaration block;
  • variables declared via let cannot be redeclared.

Arrow functions are the abbreviated spelling of ordinary functions:

 //1.   const func1 = function() { return 'test'; } //    const func1 = () => 'test'; //2.   const func2 = function(arg) { if (arg > 0) return 'plus'; else return 'minus'; } //    const func2 = arg => { if (arg > 0) return 'plus'; else return 'minus'; } //3.   const func3 = function(arg1, arg2){ if (arg1 > arg2) return arg1; else return arg2; } //    const func3 = (arg1, arg2) => { if (arg1 > arg2) return arg1; else return arg2; } 


Now that we understand how to use the Custom Template API, we can write the code for how the tag works using the JavaScript syntax ES6.

My code contains methods for launching a pixel, installing VK openapi.js, retrieving data about a product from a dataLayer (when configured on the extended e-commerce site of Google), processing this data to bring them into the form required to send retargeting VKontakte to pixel , and the method of sending the event.

The pixel trigger method supports three scenarios:

  1. Pixel runs on the page where openapi.js is missing.
  2. Pixel is launched on the page where there is openapi.js, but it has not loaded yet.
  3. Pixel runs on a page loaded with openapi.js.

The full code of my custom GTM template for a pixel dynamic retargeting VKontakte
 //api const copyFromWindow = require('copyFromWindow'); const setInWindow = require('setInWindow'); const injectScript = require('injectScript'); const callInWindow = require('callInWindow'); const makeTableMap = require('makeTableMap'); const getUrl = require('getUrl'); const getQueryParameters = require('getQueryParameters'); const makeInteger = require('makeInteger'); const makeString = require('makeString'); const callLater = require('callLater'); //    const settings = { event: data.event, pixelIDs: data.pixelIDs, priceListId: data.priceListId, fewPriceLists: data.fewPriceLists, priceListIds: data.priceListIds === undefined ? data.priceListIds : makeTableMap(data.priceListIds,'hostname','priceListId'), ecommerceUse: data.ecommerceUse, eventEcommerce: data.eventEcommerce, siteSearchQueryParam: data.siteSearchQueryParam }; //       const pixel = { //    getPageHostname: () => getUrl('host'), //    VK getVK: () => copyFromWindow('VK'), //    VK setVkAsyncInit: () => { setInWindow('vkAsyncInit', pixel.sendEvent); }, //       getSiteSearchPhrase: () => { if (settings.event === 'view_search') return getQueryParameters(settings.siteSearchQueryParam); else return undefined; }, //      getEventParams: (products, currencyCode, revenue) => { let eventParamsClean= {}; let eventParams = { products: eventProducts.getProductParams(products), category_ids: eventProducts.getCategoryString(products), currency_code: currencyCode, total_price: eventProducts.getTotalPrice(products, revenue), search_string: pixel.getSiteSearchPhrase() }; if (eventParams.products !== undefined) eventParamsClean.products = eventParams.products; if (eventParams.category_ids !== undefined) eventParamsClean.category_ids = eventParams.category_ids; if (eventParams.currency_code !== undefined) eventParamsClean.currency_code = eventParams.currency_code; if (eventParams.total_price !== undefined) eventParamsClean.total_price = eventParams.total_price; if (eventParams.search_string !== undefined) eventParamsClean.search_string = eventParams.search_string; return eventParamsClean; }, //  - getPriceListId: hostname => { if (settings.fewPriceLists) return settings.priceListIds[hostname]; else return settings.priceListId; }, //  openapi.js openapiInit: () => { injectScript('https://vk.com/js/api/openapi.js?159', pixel.setVkAsyncInit(), data.gtmOnFailure, 'vkPixel'); setInWindow('openapiInject', 1); }, //   sendEvent: () => { if (settings.event === 'hit') { settings.pixelIDs.split(',').forEach(p => { callInWindow('VK.Retargeting.Init',p); callInWindow('VK.Retargeting.Hit'); }); } else { const pricelist = pixel.getPriceListId(pixel.getPageHostname()); const name = settings.event; let products = []; if(settings.ecommerceUse) products = name === 'view_home' || name === 'view_category' || name === 'view_search' || name === 'view_other' ? settings.eventEcommerce : settings.eventEcommerce.products; else products = undefined; const currencyCode = settings.ecommerceUse ? settings.eventEcommerce.currencyCode : undefined; const revenue = (settings.ecommerceUse && name === 'purchase') ? settings.eventEcommerce.actionField.revenue : undefined; const eventParams = settings.ecommerceUse ? pixel.getEventParams(products, currencyCode, revenue) : undefined; settings.pixelIDs.split(',').forEach(p => { callInWindow('VK.Retargeting.Init',p); callInWindow('VK.Retargeting.ProductEvent', pricelist, name, eventParams); }); }, //   start: () => { if (pixel.getVK() === undefined && copyFromWindow('openapiInject') !== 1) { pixel.openapiInit(); data.gtmOnSuccess(); } else if (pixel.getVK() === undefined && copyFromWindow('openapiInject') === 1) { if (pixel.count < 50) { callLater(pixel.start); pixel.count++; } else return; } else { pixel.sendEvent(); data.gtmOnSuccess(); }, //   count: 0 }; //       const eventProducts = { //    products   getProductParams: products => { let arr = []; products.forEach(i => { let productParamsClean = {}; let productParams = { id: makeString(i.id), group_id: makeString(i.brand), price: makeInteger(i.price * 100) / 100 }; if (productParams.id !== 'undefined') productParamsClean.id = productParams.id; if (productParams.group_id !== 'undefined') productParamsClean.group_id = productParams.group_id; if (productParams.price !== 0) productParamsClean.price = productParams.price; arr.push(productParamsClean); }); return arr; }, //       'a,b,c'       getCategoryString: products => { let categoryId = ''; let check = []; products.forEach(i => { if(check.indexOf(i.category) === -1) { check.push(i.category); categoryId += ',' + i.category; }); return categoryId.slice(1); }, //     getTotalPrice: (products, revenue) => { let sumPrice = 0; if (revenue !== undefined ) return makeInteger(revenue * 100) / 100; else { products.forEach(i => { if (i.hasOwnProperty('quantity')) sumPrice += (makeInteger(i.price * 100) / 100) * makeInteger(i.quantity); else sumPrice += makeInteger(i.price * 100) / 100; }); return sumPrice; }; //  pixel.start(); 


Permissions tab


After writing the tag code, the final stage remains - to issue permissions to interact with the global data of the page. This is done just on the Permissions tab.

Since the code is executed in an isolated environment, and interaction with global data occurs through the API, for each API method (where necessary) we need to manually specify permissions for certain actions.

This is done in order to take the most thoughtful approach to working with the global data of the page, and, thereby, to minimize the chances of “putting” the site in error in the code of our template.

For the API methods used in my code, you need to issue three types of permissions:



1. Accesses Global Variables - access to read, write, execute global variables that are used in our code. Variables must be added manually and for each of them indicate what we are allowed to do.



For example, the VK variable can only be read, vkAsyncInit can be read and redefined, and the VK.Retargeting.Hit method can only be executed.

2. Reads URL . Here you need to specify which parts of the URL are allowed to receive. I allow to receive any parts of the URL:



But if you want, you can specify some specific ones:



3. Injects Scripts . Here it is necessary to register addresses from which external scripts can be downloaded. In my code, only one script is loaded with VK openapi.js, and I indicate its address:



That's it, the Custom Template setting is complete, you can save the template and proceed to testing.

Customize and test the tag according to the created Custom Template


As an example, create two tags of dynamic retargeting “VKontakte” using the created Custom Template: pageview and addToCart.

Pageview


Go to the desired GTM container, create a new tag, select the tag type VK Pixel in the Custom section:



Fill in the tag name, track the event, select Hit (this is the standard pageview), specify the ID of two pixels in the “Pixel ID” field and send the data to the ID, and set the All Pages trigger:



Save the created tag.

AddToCart


Creating a tag for the event of adding a product to the cart will be a bit more complicated than the Hit tag.

First, it is necessary to transfer the goods that are added to the basket. As I wrote above, this is possible with Google’s advanced e-commerce site configured. In this case, the product data is taken from the dataLayer.

To do this, we need to create a dataLayer variable in GTM that will hold the ecommerce object for the addToCart event. Variable settings look like this:



Secondly, you need to create a trigger that will activate the tag when the ecommerce addToCart event occurs (the trigger will activate the tag when you push in the dataLayer at the addToCart event):



After creating the variable with the ecommerce object and the trigger, you can start creating the tag:



In order:

  1. As a tracked event, select Add To Cart.
  2. Fill in a comma-separated ID of two pixels to which you want to transfer data.
  3. Set the checkbox “Use multiple price lists”: for Moscow and St. Petersburg, in our example, you need to use different price lists.
  4. Fill the table with price lists.
  5. Check the box "Use ecommerce to transfer products and options."
  6. In the ecommerce object of this event, specify the variable created earlier.
  7. Set the trigger on the event being monitored, in this case AddToCart.
  8. We save.

Testing check


To check the working off of pixels of dynamic retargeting “VKontakte” you need to activate the Preview mode in GTM, go to our website and open the Network section in the browser console and in the Filter field enter 'rtrg':


After that we update the page, and we should have two requests - a Hit event sent in two pixels:



Status 200 means that requests are sent and received by the server successfully.

Also in the Preview GTM window we can see that our created tag worked correctly for the Page View event.

To check the Add To Cart event, we add the item to the cart, and two more requests appear in the console:



In the Preview GTM window we see that the second tag worked successfully. Data about the product from dataLayer was pulled up and processed correctly, the correct price list was also substituted.

For the second host, the price list is also correctly substituted:



Tags for other events are configured and checked in the same way.

Conclusion


Custom patterns change the familiar GTM usage paradigm. Everyone is used to HTML tags and JS variables, but now there is a great alternative.

It is enough to create a high-quality user template once, and then anyone who is familiar with GTM will be able to use it.

Given the opportunity to share the created templates, I think they should gain popularity among users.

You can download the custom VKontakte dynamic retargeting pixel template that we discussed in this article.

To import a template, you need to create a new Custom Template and select Import in the menu:



Material prepared by me for the publication of ppc.world

Source: https://habr.com/ru/post/458788/


All Articles