Assign data dynamically onReady to repeater

Question:
Need help understanding why setting $w(‘#myRepeater’).data = repeaterData; not applying data to the fields in a repeater.

Product:
Wix Studio Editor

What are you trying to achieve:
Using dynamic pages in conjunction with multiple CMS collections. In this portion of the page I’m using a repeater to display an image “iconImg” and text “iconTxt”, combining data from 2 CMS collections. Running the code below results in showing 3 repeater elements displayed (that’s good, as it only has 1 by default), but the repeater only shows the default repeater image (#iconImg ) and default repeater text (#iconTxt ).

Code and console output below. The relationship of CMS collections, datasets, and repeater data still not clear to me - and also onReady and onItemReady. Feel like there must be some best practices to be able to use dynamic coding, Suggestions welcome. I learn well with examples, but wix/vloe examples are either too simple or over complicated. Thanks!

import wixData from ‘wix-data’;

$w.onReady(async function () {
$w(“#listings”).onReady(() => {

    let itemObj = $w("#listings").getCurrentItem();

    //  
    $w("#PropertyIconsDataset").onReady(() => {

        let repeaterData = [];
        wixData.query("repeaterIcons")
            .find()
            .then(results => {
                results.items.forEach(item => {
                    let p = "";
                    let iconTxt = item.title;
                    let iconImg = item.image;

                    p = itemObj.numBedrooms;
                    if (iconTxt === "Bedrooms" && (p !== undefined || p !== null || p !== '')) {
                        repeaterData.push({ _id: '1', iconTxt: "Beds " + p, iconImg: iconImg });
                    }

                    p = itemObj.numBaths;
                    if (iconTxt === "Baths" && (p !== undefined || p !== null || p !== '')) {
                        repeaterData.push({ _id: '1', iconTxt: "Bath " + p, iconImg: iconImg });
                    }
                });
                  $w('#myRepeater').data = repeaterData;  
                 console.log("Repeater Data Set: ", $w('#myRepeater').data);
              
            })
            .catch(error => {
                console.error('Error fetching data:', error);
            });
    });

    $w("#myRepeater").onItemReady(($item, itemData, index) => {

    });

});

}); // onReady

Console output:
Repeater Data Set onready:
Array(3)jsonTableCopy JSON
0: {…}jsonTableCopy JSON
_id: “3”
iconTxt: “2076 SF”
iconImg: “wix:image://v1/d177e4_525c977fa4f54c8fbd3cbe5dcc3c38d8~mv2.png/ lot.png#originWidth=200&originHeight=162”
1: {…}jsonTableCopy JSON
_id: “2”
iconTxt: “Bath 2”
iconImg: “wix:image://v1/d177e4_b47ab08620db42baab408ebce16e74fa~mv2.png/ bath.png#originWidth=200&originHeight=162”
2: {…}jsonTableCopy JSON
_id: “1”
iconTxt: “Beds 3”
iconImg: “wix:image://v1/d177e4_eb1cd32e30e948aa817f1e7a29c0d5fb~mv2.png/ bed.png#originWidth=200&originHeight=162”

Also - not sure how to control the sorting order of the repeater. Thank you for any help!

The repeater.onItemReady() function is an event listener and would only listen for “Item Ready” events in the code blocks after it is declared. Hence, you would need to create the listener before setting repeater data, which is what causes the “Item Ready” event.
If you put the repeater.onItemReady() block above your wixData.query, you should start seeing your data appear.

Thanks for the response! Would you have time to help with a couple questions, as I’m still missing the behavior of repeater.

My goal is to gather data from various collections for a dynamic page(s) and once gathered apply to various repeaters. Maybe this approach in fundamentally inferior to better approach.

  1. You suggest “to create the listener before setting repeater data”. I have tried putting $w(“#myRepeater”).onItemReady at the top, but above it is never called. Maybe I do not understand “above”, do you mean sequentially above or nested outside my $w(“#PropertyIconsDataset”).onReady(()/query?

  2. If I understand correctly, when data assignment happens ($w(‘#myRepeater’).data = repeaterData) a callback should be called to: $w(“#myRepeater”).onItemReady. If correct, then for some reason the assignment is not working ( $w(‘#myRepeater’).data = repeaterData ). The repeaterData array is correct - but the assignment appears not to be happening. I do not understand why not? Clearly missing something important.

Here is my latest code:

$w.onReady(async function () { // I have tested with async and not async

$w("#myRepeater").onItemReady(($item, itemData, index) => {

    console.log("onItemReady myrepeater"); // never gets called
    $item("#iconimg").src = itemData.iconimg;
    $item("#icontxt").text = itemData.icontxt;
});


$w("#listings").onReady(() => {

    let itemObj = $w("#listings").getCurrentItem();
    updateHeader(itemObj);

    $w("#PropertyIconsDataset").onReady(() => {

        let c = 0;

        wixData.query("repeaterIcons")
            .find()
            .then(results => {
                results.items.forEach(item => {
                    let p = "";
                    let icontxt = item.title;
                    let iconimg = item.image;

                    p = itemObj.numBedrooms;
                    if (icontxt === "Bedrooms" && (p !== undefined && p !== null && p !== '')) {
                        repeaterData.push({ _id: c, icontxt: "Beds " + p, iconimg: iconimg });
                        c++;
                    }

                    p = itemObj.numBaths;
                    if (icontxt === "Baths" && (p !== undefined && p !== null && p !== '')) {
                        repeaterData.push({ _id: c, icontxt: "Bath " + p, iconimg: iconimg });
                        c++;
                    }
                    
                // several more conditionals happen here 

                });
                $w('#myRepeater').data = repeaterData;
               // This assignment seems not to work - even though the repeaterData looks good
                console.log("query repeaterData", repeaterData, "$w('#myRepeater').data", $w('#myRepeater').data);

            })
            .catch(error => {
                console.error('Error fetching data:', error);
            }); // end query

    }); // $w("#PropertyIconsDataset").onReady  

}); //$w("#listings").onReady(()

}); // onReady

Value of:

Array(3)jsonTableCopy JSON
0: {…}jsonTableCopy JSON
_id: “3”
iconTxt: “2076 SF”
iconImg: “wix:image://v1/d177e4_525c977fa4f54c8fbd3cbe5dcc3c38d8~mv2.png/lot.png#originWidth=200&originHeight=162”
1: {…}jsonTableCopy JSON
_id: “2”
iconTxt: “Bath 2”
iconImg: “wix:image://v1/d177e4_b47ab08620db42baab408ebce16e74fa~mv2.png/bath.png#originWidth=200&originHeight=162”
2: {…}jsonTableCopy JSON
_id: “1”
iconTxt: “Beds 3”
iconImg: “wix:image://v1/d177e4_eb1cd32e30e948aa817f1e7a29c0d5fb~mv2.png/bed.png#originWidth=200&originHeight=162”

The relationship between collections, datasets, and elements and the associated onReady’s are unclear to me. I really want to exploit the power of wix but need education and not sure where to get this level (I think very intermediate - not advanced - just slightly past what the basic documentation has). (Yes, I have read lots of wix docs, youtube vids, etc - but clearly not enough or the right ones :wink:

Feeling both idiotic and frustrated - ha! - and thankful this forum is here to try and help!

Happy to help as I was stuck on these for quite a bit as well haha. Here I’m speaking from my short experience trying to brute force the repeaters to work with dynamic datasets so I might not be 100% on the inner workings:

  1. For the onItemReady listener, I found that it worked as long as you set the listener before you call $w(“#repeater”).data = newData. In my functions, I had the listener set right before setting data, although where you set it shouldn’t matter, as long as the onItemReady() is called before setting repeater data.

I strongly believe your onItemReady() function is perfect, I don’t see any problem with it and I think the issue is with (2). But just in case you want to confirm you could try adding a console.log right after to ensure it is being set? E.g.

$w("#myRepeater").onItemReady(($item, itemData, index) => {

    console.log("onItemReady myrepeater"); // never gets called
    $item("#iconimg").src = itemData.iconimg;
    $item("#icontxt").text = itemData.icontxt;
});
console.log("Awaiting repeater data change...")
  1. Regarding data assignment or setting repeater data, from what I understood from the docs, setting repeaterData should replace all items in the repeater and this code does not look too dissimilar to mine.

Just in case, did you also use the Wix Editor Connect to CMS function? The squiggly line when you try to edit the repeater element via the editor. If it’s green (i.e. connected to a dataset) I believe that the Editor CMS function overrides the set data, maybe cause it gets called last?

For reference, this is how I did my repeater:

// userQuery is a WixDataQuery
async function setupRepeater(userQuery) {
	const noRequestText = $w("#noRequestText2")
	const requestRepeater = $w("#completedRequestRepeater")

    const userItems = await userQuery.find()
    if (userItems.length == 0) return
    // Clearing data just in case, technically redundant code.
	requestRepeater.data = []
    requestRepeater.onItemReady(($item, itemData) => {
        //Add data to repeatElement
        $item("#productName2").text = itemData.productName
        $item("#productDescription2").text = itemData.productDescription
        $item("#productImages2").items = itemData.productImages
	})
	requestRepeater.data = userItems
}

Honestly, I feel that the onReady documentations are simple enough, they say what the function do, but the docs do not really cover how code runs when loading the site or how Velo might be affected by the base Wix functionalities, such as WixData to set data in elements vs using CMS connections. A lot of the tutorials and videos cover very simple cases and don’t go very in depth so I’ve had to do a lot of experimentation on my own to figure out how to do stuff as well so if you’re stuck, I’d advise to keep searching or keep trying!

Hi Steve. Have you been able to solve this issue? I recently got the Wix velo certification so I guess I’m pretty experienced with Velo. However, the way I programmatically interact with Wix’s CMS is quite different from your manner of approach- the issue in your code isn’t from the onReady function and there’s no need for asynchronous functions too as you aren’t dealing with promises, I’d prefer to write a whole new bunch of more straightforward code for the functionality you require.
I’ll love to help, let me know if you still need assistance.

I see a lot of difficulties here and also major failures working with datasets, dynamic datasets and Wix-Data.

In most cases (also in your case) the problem starts, whenever someone tries to mix datasets with wix-data, especially in combination with DYNAMIC ones.

You should first make clear your setup!

You have…

  1. ONE DYNAMIC PAGE WHICH IS CONNECTED TRHROUGH A DYNAMIC DATASET.
    YOU MUST UNDERSTAND THAT ON DYNAMIC PAGES —> ONLY <------> ONE <— ITEM CAN BE SHOWN → AT ONCE, BECAUSE EACH OF YOUR ITEMS COMES FROM YOUR DATABASE YOU CONNECTED TO (COLLECTION).

Whenever you do this… (by the way you are using very bad variable identifications/definitions)…you must know that you are loading only ONE item (row-line) out of your database, on a DYNAMIC-PAGE.

let itemObj = $w("#listings").getCurrentItem();

That means → listings <— is your → DYNAMIC-DATASET.

  1. The one thing you did right is first to wait for a DATASET until it has been completely loaded before using it.

  2. Reading “Justin1315”-comments, he points you already into the right direction, by (assuming) telling you the following…

Just in case, did you also use the Wix Editor Connect to CMS function? The squiggly line when you try to edit the repeater element via the editor. If it’s green (i.e. connected to a dataset) I believe that the Editor CMS function overrides the set data, maybe cause it gets called last?

He already did recognize something, the fact that as soon as you work inside of the PROPERTY-PANEL of your DATASET —> it has it own process-flow which will make trouble.

This is why i wrote something about → to NOT mixing coding way with settings inside the property-panel of your dataset. Go either the one way or the other.

To make all your setup more simple, i would suggest you to use Wix-Data and define the data of different databases in several VARIABLES, by using queries, like…

…like shown here…

Once you have defined your variables and filled them with data…

let myDataFromDatabase1 = myQueryFunction1();
let myDataFromDatabase2 = myQueryFunction2();
let myDataFromDatabase3 = myQueryFunction3()

;

You can write 3 different functions for 3 different databases to query, like…

function myQueryFunction1() {
    ....my query-code here....
    ...at the end your function returns back the result-data... 
    return results.items
}

function myQueryFunction2() {
    ....my query-code here....
    ...at the end your function returns back the result-data... 
    return results.items
}

function myQueryFunction2() {
    ....my query-code here....
    ...at the end your function returns back the result-data... 
    return results.items
}

Or you generate a GENERAL-FUNCTION, which will be able to query any database you want…

function getData(db, dbField, value) {
    return wixData.query(db)
    .eq(dbField, value)
    .find()
    .then((results) => {
       if(results.items.length > 0) {
            console.log('Data has been found!');
            return results.items;
       } 
       else {console.log('No data has been found!'}
    })
    .catch((err) => {console.log(err);});
}

Now let us get the data from database-1 getting data from the TITLE-Field…(using the GENERAL-FUNCTION)…

let myDB1data = await getData(db, dbField, value);

As you can see something new was added to our code…

—> AWAIT <---- what is it ? Why do we need this? And what else is missing?

As soon as you see → AWAIT <— somewhere inside of your codes, make sure that you also define a corresponding → ASYNC <— which will make your code working → ASYNCHRONOUS!

Also make some thoughts about what —> ASYNCHRONOUS means!

So let us put the first part of our PUZZLE together…

We start LIKE ALWAYS with…

$w.onReady(()=>{
    ...
});

Followed by…

$w('#dynamicDataset').onReady(()=>{
    ...
}

…since we are working on a dynamic dataset…

$w.onReady(()=>{
    $w('#dynamicDataset').onReady(()=>{
        ...the rest of code...
    }
});

Putting all together…

$w.onReady(()=>{
    $w('#dynamicDataset').onReady(async()=>{
         //------------------------------------------------
         let curItem = $w("#dynamicDataset").getCurrentItem(); 
         console.log('Current-Item: ', curItem);
         //------------------------------------------------
          let myDatabase1Data =  await getData('Collection1', 'Title', 'myValueXHere');
          let myDatabase2Data =  await getData('Collection2', '_id', 'myIdValueHere');
          let myDatabase3Data =  await getData('Collection3', 'WHATEVER-FIELD-YOU-WANT', 'myValueZHere');
    }
});

//######### FUNCTIONS ###########
function getData(db, dbField, value) {
    return wixData.query(db)
    .eq(dbField, value)
    .find()
    .then((results) => {
       if(results.items.length > 0) {
            console.log('Data has been found!');
            return results.items;
       } 
       else {console.log('No data has been found!'}
    }) .catch((err) => {console.log(err);});
}

Now you do not need any DATASETs exept the DYNAMIC one, anymore, since everything will be managed by dynamic queries for each of your wished databases.

Using → DATASETS → means STATIC-PROCESS → connecting each of dataset possible to only one of databases.

Using CODE-VERSION → makes everything → DYNAMIC → now you can do what ever you want.

FORGET ABOUT DATASETS! (if you want to go totaly dynamic)

Next step would be to → FEED ← your REPEATERS with corresponding DATA!

You have now 3 different VARIABLES holding the needed data of your various databses…
—> myDatabase1Data
—> myDatabase2Data
—> myDatabase3Data

Now chose for example one of those and push it to the REPEATER of your choice…

  $w('#myRepeater1').data =  myDatabase1Data;

Do the same for others…

  $w('#myRepeater2').data =  myDatabase2Data;
  $w('#myRepeater3').data =  myDatabase3Data;
  1. The last thing you will need is…like already mentioned → onItemReady()
$w("#myRepeater1").onItemReady(($i, iData, i) => {
    console.log("Item-Data: ", iData);
    $i("#imageElement").src = iData['property'];
    $i("#textElement").text = iData['property'];
});

Do the same for other 2x repeaters…

$w("#myRepeater2").onItemReady(($i, iData, i) => {
    console.log("Item-Data: ", iData);
    $i("#imageElement").src = iData['property'];
    $i("#textElement").text = iData['property'];
});

$w("#myRepeater3").onItemReady(($i, iData, i) => {
    console.log("Item-Data: ", iData);
    $i("#imageElement").src = iData['property'];
    $i("#textElement").text = iData['property'];
});

And so on… —> complete the code…