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?
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,
};
i have some simple vuex store with
const state = {
todos : []
}
const getters = {
allTodos: (state) => state.todos
}
const actions = {
async fetchTodos({ commit }) {
console.log(this.state.todos)
if(state.todos.length == 0) {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos?_limit=5')
commit('setTodos', response.data)
}
}
}
const mutations = {
setTodos(state, todos) {
state.todos = todos
}
}
why does console.log in fetchTodos action output populated todos before it was populated with axios.get and setTodos mutation?
when i write
const actions = {
fetchTodos({ commit }) {
console.log(this.state.todos)
setTimeout(async () => {
if(state.todos.length == 0) {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos?_limit=5')
commit('setTodos', response.data)
}
}, 10000)
}
}
output is normal with empty todos in state
That's because you will see a little blue triangle right next to the console log. I don't know the technical term for it but what happens is that the browser will update that variable with the current value because it is a reactive variable and since it is a reference being pointed to a location in memory, it will update.
If you truly wish to see the value and prove what was described above, you can write:
console.log(JSON.parse(JSON.stringify(this.state.todos)));
What I am trying to do is sync a list of attendees from an online database, and if the current user is in the list, then disable a button, else enable the button.
I am using react native hook (I am not sure if I am using the term correctly as I am fairly new to react), in order to set the value of disabling the button.
The issue that I am facing is that the value is getting initialized to false, even tho it should clearly get initialized to true.
After adding some logging I made sure that the function is executing correctly and reaching the code where it sets the value to true.
const [buttonDisabled, changeButtonState] = useState( () => {
var database = firebase.database();
var userId = firebase.auth().currentUser.uid;
const dbRef = firebase.database().ref();
var Attendees = [];
var disable = false;
dbRef.child("gameAttendees").child(gameinfo.gameID).get().then((snapshot) => {
if (snapshot.exists()) {
Attendees = snapshot.val().Attendees;
for(var i=0;i<Attendees.length;i++){
if(Attendees[i]==userId){
return true;
}
}
} else {
console.log("no value");
return false;
}
}).catch((error) => {
console.error(error);
});
});
Adding an example of an async mount effect:
const Comp = () => {
const [s, setS] = useState(); // State will be undefined for first n renders
useEffect(() => {
// Call the async function and set the component state some time in the future
someAsyncFunction().then(result => setS(result));
}, []); // An effect with no dependencies will run only once on mount
return </>;
};
I have a composable that fetches and returns ref documents in real-time from Firestore. The ref contains data but when I call .value in the setup(), null is returned.
The composable is as follows.
import { watchEffect, ref } from "vue";
import { projectFirestore } from "../firebase/config";
const getDocument = (collection, id) => {
let document = ref(null);
let error = ref(null);
let documentRef = projectFirestore.collection(collection).doc(id);
const unsub = documentRef.onSnapshot(
doc => {
if (doc.data()) {
document.value = { ...doc.data(), id: doc.id };
error.value = null;
} else {
error.value = "That document does not exist.";
}
},
err => {
console.log(err.message);
error.value = "Problem fetching the document.";
}
);
watchEffect(onInvalidate => {
onInvalidate(() => unsub());
});
return { error, document };
};
export default getDocument;
When I dump the result of this, console.log(result), I get the output in the capture below but result.value is null.
How can I get the values within setup()?
Please note that the capture is a result of console.log(result) & console.log(result.value).
I am new to ES6 and react-native, trying to get multiple values from the SecureStore.
I think I am misunderstanding promises here ... global.userData is empty in the Promise.all(promises).then function. The relevant values do exist in the secure store
My code is:-
getUserData(fields) {
var promises = [];
var that = this;
global.userData = {};
function getField(field) {
return SecureStore.getItemAsync(field)
.then(res => {
console.log(field+"="+res); // this appears after the log below
global.userData[field] = res;
})
.catch(error => {
global.userData[field] = null;
});
}
fields.map(field => {
promises.push[getField(field)];
});
Promise.all(promises).then(function(v) {
console.log(global.userData); // this is empty
that.setState({ isReady: true }); // allow page to render
});
}
getUserData(["userId", "userName","etc"]);
My bad ... inadvertantly used
promises.push[getField(field)];
should have been:
promises.push(getField(field));
Suprised it wasn't detected as a syntax error ...