I have a number of VectorImage icons on a page, for which I wish to add an onClick event. Each icon, when clicked, will display information from a simple database collection. I have achieved this by using a .forEach loop on all VectorImages, but am now looking to refine this code as my page may contain other VectorImages which are not part of the collection.
My current thinking is to create an array containing just the icon id's and then loop through this in the same way as I did with the VectorImages. Rather than "hard-coding" the array, I am querying my collection and pushing each id into the array. Again, this works fine in principle, but as it is a query, there is a delay and my subsequent .forEach loop starts before the array is populated and the loop tries to run on an empty array. I have tried nesting the entire .forEach loop inside the .then() code, but I get an error: "icon.onClick is not a function".
I'm hoping that someone is able to offer a neat solution to this but am also keen to understand why my attempts don't work. I'm a relative newcomer to Velo, but learning quickly 😀
My code is below:
let iconList = [] // initialise empty iconList array wixData.query("places") // create query for the "places" collection .find() // run the query .then((results) => { results.items.forEach(entry => { // loop through each entry in the collection iconList.push("$w('#" + entry.icon + "')") // push the icon id to the iconList array }) // this is where I tried inserting the Assign onClick events loop below }) // Assign onClick events // $w("VectorImage").forEach(icon => { // loop through all vector images on page (my original method) iconList.forEach(icon => { // loop through all items in the iconList array (my new method) icon.onClick((event) => { // create "on click" event for icon wixData.query("places") // create query for the dataset .eq("icon", icon.id) // filter criteria icon = clicked icon .find() // run the query .then(res => { $w('#text1').text = res.items[0].title // set text1 element = title $w('#text2').text = res.items[0].description // set text2 element = description let flyEffects = { // set animation effect "direction": "top", "duration": 200 } $w('#info').show("fly", flyEffects) // show info box, using animation effects }) }) })
You got the concept right, let's refactor a little bit to make it work in a cleaner way.
Using async/await you can easily await for the data before doing anything else. It also allows you get rid of the scoped .then() method.
import wixData from 'wix-data' const flyEffects = { // set animation effect direction: 'top', duration: 200, } const queryIcons = async () => { const { items } = await wixData.query('places').find() //Destructuring the query result to get the items array return items.length > 0 ? items.map((item) => `item.icon) : null //Map the items array to get the icon property of each item } const clickAction = async (e) => { //Click action that will be executed when the user clicks on the button const { id } = e.target const { items } = await wixData .query('places') // create query for the dataset .eq('icon', id) // filter criteria icon = clicked icon .find() if (items.length > 0) { $w('#text1').text = items[0].title // set text1 element = title $w('#text2').text = items[0].description // set text2 element = description $w('#info').show('fly', flyEffects) } // show info box, using animation effects } $w.onReady(async () => { const iconsList = await queryIcons() iconsList.forEach((icon) => { $w(`#${icon}`).onClick(clickAction) //The error was here, the WIX Element syntax was wrong }) // add click event to each icon })