vuejs2google-tag-managervue-composition-apivue-apollo

vue apollo 2 composition api track result


So I am trying to add product impressions to my site by following this article:

https://developers.google.com/tag-manager/enhanced-ecommerce#product-impressions

I have created a bit of logic to fire off the required data like this:

import { getCurrentInstance } from "@vue/composition-api";

import { useGtm } from "@gtm-support/vue2-gtm";

export function useTrackProductImpressions(items: any[]) {
  console.log("trying to track products", items);

  if (!items?.length) return;

  const gtm = useGtm();
  if (!gtm.enabled()) return;
  const dataLayer = window.dataLayer;

  if (!dataLayer) return;

  console.log(items);

  const products = items.map((product, i) => {
    const retailers = product.retailers ?? [];

    return {
      name: product.title, // Name or ID is required.
      id: product.id,
      price: retailers[0].price,
      brand: product.brand,
      category: product.categorySlug,
      variant: product.variant,
      position: i,
    };
  });

  const instance = getCurrentInstance();
  const route = instance.proxy.$route;
  const routeName = route.meta?.title ?? route.name;

  dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object.
  dataLayer.push({
    event: "productClick",
    ecommerce: {
      click: {
        actionField: { list: routeName }, // Optional list property.
        products,
      },
    },
    // eventCallback: function () {
    //   document.location = productObj.url;
    // },
  });
}

This seems pretty normal and I have a click version of this that works fine. The problem is, the click event can be fired when a link is clicked, this one needs to fire when the view loads, I assume in setup.

So, I have my apollo logic:

import { useQuery, useResult } from "@vue/apollo-composable";
import * as listProducts from "@graphql/api/query.products.gql";

export const defaultParameters: {
  identifier?: string;
  searchTerm: string;
  itemsToShow: number;
  page: number;
  filters: any;
  facets: string[];
} = {
  searchTerm: "*",
  itemsToShow: 12,
  page: 1,
  filters: [],
  facets: ["Criteria/Attribute,count:100"],
};

export function useSearchProducts(params) {
  const { result, loading, error, fetchMore } = useQuery(listProducts, params);
  const response = useResult(result, null, (data) => data.searchProducts);
  return { response, loading, error, fetchMore };
}

And from my setup I invoke like this:

const { category } = toRefs(props);
const page = ref(1);
const skip = ref(0);
const orderBy = ref([
  {
    key: "InVenue",
    value: "desc",
  },
]);

const params = computed(() => {
  const filters = createFilters("CategorySlug", [category.value.slug]);
  const request = createRequest(
    defaultParameters,
    page.value,
    filters,
    orderBy.value
  );
  return { search: request };
});

const { response, loading, error} = useSearchProducts(params);

Which I can then return to the template like this:

return { response, loading, error };

Now I have done this, I want to add some tracking, so initially I did this:

watch(response, (result) => useTrackProductImpressions(result?.value?.items));

But it was always undefined. I added console log on result within the watch method and it is always undefined. So I changed to this:

const track = computed(() => {
  useTrackProductImpressions(response.value.items);
});

But this never gets invoked (I assume because it has no return value and I don't use it in the template).

My question is, which is the best way to do what I am attempting? Am I missing something or am I on the write track?


Solution

  • I think I was close, I just used the computed property to return my products like this:

    const products = computed(() => {
      if (!response.value) return [];
      useTrackProductImpressions(response.value.items);
      return response.value.items;
    });
    
    const total = computed(() => {
      if (!response.value) return 0;
      return response.value.total;
    });
    
    const hasMoreResults = computed(() => {
      if (!response.value) return false;
      return response.value.hasMoreResults;
    });
    
    return {
      products,
      loading,
      error,
      total,
      hasMoreResults,
      skip,
      more,
      search,
    };