Search code examples
eventse-commercegoogle-tag-managergoogle-analytics-4data-layers

Overwriting specific dataLayer values, then re-pushing the dataLayer


I've got a GTM container on a third party donation processing system that passes along a GA4 purchase event with ecommerce data when a donation is successfully processed. The problem is that the purchase item information they pass is basically unreadable to the end report user.

I want to change some specific values like the item_id, item_name, item_brand, etc. to more readable formats, while preserving the other values to be passed onto GA4 as a whole.

I wound up creating a custom HTML tag that updates the values when the purchase event is triggered, then fires a new final_purchase event which triggers the GA event tag to send everything over to GA4:

<script type="text/javascript">
  (function() {
      dataLayer.push({
        'ecommerce': {
          'affiliation': 'POS company',
          'items': [{
            'item_id': '{{donation ID}}',
            'item_name': '{{donation type name}}',
            'item_brand': 'Recipient Org',
            'item_category': 'Donations'
          }]
        },
        'event': 'final_purchase'
      });
  })();
</script>

This initially seemed to work fine, GTM's debug view was showing the dataLayer values as a combination of the original data + my updates, as intended:

ecommerce: {
    transaction_id: "12345678",
    affiliation: "POS company",
    value: 10,
    currency: "USD",
    items: [
      {
        item_id: "DON_1234",
        item_name: "One-Time General Donation",
        price: "10",
        item_brand: "Recipient Org",
        quantity: 1,
        item_category: "Donations"
      }
    ]
  }

But over in GA4, the debugView is ONLY displaying the information from my overwrites (behaving as if the DLV is v1 and all the original data was overwritten; note the missing price, and the transaction_id, value, and currency parameters also weren't received):

screencap of GA4 debugview results missing initially populated item info like price

So I think rather than just pushing changes to selective values, I need to grab the ecomm section of the DLV, update the values I want, and then push the entire section back or else GA4 won't get the original values.

I tried turning the entire dataLayer into a custom JS variable called fullDataLayer and then attempted this:

<script type="text/javascript">
  (function() {
      dataLayer.push({
        'ecommerce': {
          'affiliation': 'POS company',
          'items': [{
            'item_id': '{{donation ID}}',
            'item_name': '{{donation type name}}',
            'item_brand': 'Recipient Org',
            'item_category': 'Donations'
          }]
        }});
      dataLayer.push({
        {{fullDataLayer}},
        'event': 'final_purchase'
      });
  })();
</script>

But GTM gives me the error "This language feature is only supported for ECMASCRIPT_2015 mode or better: extended object literal." I tried searching the error but I couldn't really understand the results I found because they were for completely different kinds of functions (I tried replacing {{fullDataLayer}} with function () { {{fullDataLayer}} }, and also tried casting it outside the push as var newDLV = function () { {{fullDataLayer}} } but neither worked).

Do I just need to find a way to resolve the error I'm getting, or is there a better way to go about this?


Solution

  • Was going to try getting the ecommerce data as a local JS object and updating it that way like @darreltw suggested, but my attempts wound up stopping the final_purchase event from pushing somehow.

    Instead of trying to pick everything out via JS, I just created custom GTM data layer variables for each item instead and referenced them in the function. It's ugly but seems to be working as intended, with GA4 receiving all of the relevant values:

    screenshot of data layer variable items in tag manager: ecommerce.affiliation, ecommerce.items.0.item_id, etc.

    <script type="text/javascript">
      (function() {
          dataLayer.push({
            ecommerce: {
              transaction_id: {{DLV - ecommerce.transaction_id}},
              affiliation: 'POS Company',
              value: {{DLV - ecommerce.value}},
              currency: {{DLV - ecommerce.currency}},
              items: [
                {
                  item_id: '{{donation ID}}',
                  item_name: '{{donation type name}}',
                  price: {{DLV - ecommerce.items.0.price}},
                  item_brand: 'Recipient Org',
                  quantity: {{DLV - ecommerce.items.0.quantity}},
                  item_category: 'Donations'
                }
              ]
            }
          });
          dataLayer.push({
            'event': 'final_purchase'
          });
      })();
    </script>
    

    I did figure out how to push iterated updates for arrays, which is redundant in this particular instance because there's only ever one donation "product" per transaction, but thought I'd include it here in case anyone else finds it useful:

    function updateObjArray(objArray, key, value) {
        for (var element in objArray) {
            dataLayer.push({ objArray[element][key]: value });
        }
    }
    
    updateObjArray({{DLV - ecommerce.items}}, 'item_category', 'Donations');