I have a Repeater with a button in it, and the button label and URL are connected to a data set.
I want to hide the button if the URL is missing from a row of the data.
I tried doing this in the onItemReady callback of the repeater, checking if the item's button has an empty URL but it doesn't doesn't work ... and I read in the documentation that this callback happens before the dataset is applied, so the button's URL is always empty.
I tried doing it in the onReady callback of the data by iterating through the repeater like this:
$w("#myData").onReady(() => {
$w("#myRepeater").forEachItem(($w, itemData, index) => {
if(itemData.url=="") {
$w("#myRepeaterButton").hide();
}
})
})
This also doesn't work.
I'm not sure if I'm using itemData correctly. If my data has a column "url" can I check itemdata.url for the contents of that field in this row?
I'm also not sure if I can use the global reference to the button in the repeater this way (above), inside the item iterator, as a reference to the specific instance of the button, that is in the row of the Repeater, corresponding to this row of the data. Actually that makes no sense. I'm iterating the data, not the repeater. So I'm a bit lost as to how to find the button in the row of the repeater that corresponds to a row of the data.
Thank you for any tips
Hi @Yevhen Pavliuk
Thank you for your reply!
I implemented your code into my website, however had no luck. The repeater still shows, and in addition to this, it doesn't seem to be filtering anything out. (Search results don't appear depending on input). I went in preview mode, and when I press on the button, I don't see any actions in the site code. Have you tested the code? If so could you share the page with me so I can test it for myself? Maybe we have different things in mind, or I did something wrong.
Warm regards, Dany.
Hi @Yevhen Pavliuk Thank you for your contribution and help. I have something similar to what jharuni was having issues with and wondering if you could help me? I am building a search bar and it is functional however doesn't come without its errors.
The search bar consists of: - database (holds all the listings) - repeater (displays all the listings that it's connected to)
- javascript code (filters through database using #gamename variable, on inputs command)
- input (tells code what is searched for in #gamename group) It's pretty simple, the database contains the text that I would want pulled out when searched for, the repeater is connected to that database and by default already displays all the listings without the searchbars' permission. The input specifies what item is searched for in the database category #gamename, and the code I believe filters through which makes the repeater only display what's searched for.
As mentioned earlier, the problem is that the repeater first displays all the database items even if it wasn't searched for. How can I fix this? I was considering hiding the repeater on load, and enabling it when something was searched for with a value greater than 0. Or perhaps the repeater could wait to appear until something has been entered in the search bar (best option). Could this work? If so how and can you help me create this code? Being that I am a web designer, I have little to none experience in javascript. Thank you.
Here is my current code: import wixData from 'wix-data' ; export function searchbar_keyPress(event, $w) { let searchValue = $w("#searchbar").value; $w("#gamedataset").setFilter(wixData.filter().contains('gamename', searchValue)); }
This no longer seems to work with the new corvid update, Is that possible?
It worked when I first tried it now its now working.
$w('#repeater1').onItemReady(($wInRepeater, itemData) => { const thereIsNodayAccommodation = !itemData.dayAccommodation if (thereIsNodayAccommodation) $wInRepeater('#accomtext').collapse() })
Thank you both for these contributions! This helps a lot.
Thanks! I’m glad to be of help.
This helps a lot. Understanding how and why things MIGHT be used, not just the one way I am using them, is key. Perhaps dataSet should have been called dataConnector. I actually found it annoying at first, I thought, "my database is my data set, why do I need another object to represent it" .... now I understand its purpose. Thanks.
By the way you guys have an incredibly powerful tool, I'm really impressed at how easy it is to get a smart, beautiful, site published. And I appreciate your thoughtful support.
You’re welcome!
Although it does seem obscure, it makes sense if you look at it from a different standpoint. Consider the following example that makes no use of a dataset:
import wixData from "wix-data"; $w.onReady(async () => { const buttonRepeater = $w("#buttonRepeater"); buttonRepeater.collapse(); buttonRepeater.onItemReady(($w, { url, title }) => { const button = $w("#button"); button.label = title; button.link = url; }); const { items } = await wixData.query("Things").find(); buttonRepeater.data = items.filter(({ url }) => url); buttonRepeater.expand(); });
Everything will work as expected because there’s no dataset that overrides the values.
Datasets exist for codeless experience because the majority of our users are not comfortable with code.
As you can see, a repeater can be assigned an arbitrary data.
Regarding the ready state of a dataset. As I understand, a dataset is considered ready when:
The data is loaded.
All components connected to the dataset are populated with the data.
So when the data is loaded, the repeater is assigned new data and its onItemReady callbacks get called.
After that, the step 2 happens.
Regarding $w. I’m not the one who invented the name. I assume it was a marketing decision. And there’s probably a similarity with jQuery.
I don’t think itemSelector is a good name. Normally selector is something like #id. For comparison, there are document.querySelector and document.querySelectorAll in the native JS DOM. So you query a selector to get the element(s).
I used a different name $wInRepeaterItem just to make it easier to see that it isn’t the global $w.
I hope this helps :)
Thank you that works. I still think it is a little obscure to have this documented there. If you populate a repeater with a dataset, the repeater is complete when the dataset is "ready", and that is documented in the iterator function of the repeater. I hope you agree that is obscure. And still not explicit enough to make me confident, even though it works. Anyway, thank you.
I am curious --- is there a reason or best practice you are following by using "$" in the localized selector function name? IE why is it $w rather than "wixPage" and why do you use $wInRepeaterItem rather than, say, (itemSelector, itemData) which would be easier to read IMHO ?
Thanks very much
I agree it looks wired that the values from the dataset aren’t assigned at this stage. But this is by design. See the note after onItemReady description https://www.wix.com/code/reference/$w.Repeater.html#onItemReady:
The following code works:
$w.onReady(() => { $w('#dataset').onReady(() => { $w('#repeater').forEachItem(($wInRepeaterItem, itemData) => { $wInRepeaterItem('#repeatedButton').label = 'Test' }) }) })
Thank you for responding Yevhen.
Your example works, and you've made some good suggestions also
collapse instead of hide. This didn't matter in my desktop layout but in mobile it looks nicer.
Console Logging helped me find typos. I'm used to other languages where my stupid mistakes would be fatal. Javascript is so forgiving ... the extra logging helps a lot.
But there is still some funky timing going on here. If I call collapse() or hide() it works but if I apply a special label to the button it does not work. You will never see the text in this example:
$w("#repeater1").onItemReady(($wRepeater, itemData, itemIndex) => { if (!itemData.url) { // $wRepeater("#button6").collapse(); $wRepeater("#button6").label = "you will never see this"; } })
I added some logging and I see that in the callback, the itemData is correct for this instance of the repeater but it has not YET been applied to the button. The button data is the static data from the repeater template in the Wix Editor. So after I apply a label here, it gets overwritten by the label from the data. However the collapse() (here commented out) works. I guess because the mapping of the data to the button does not reverse that.
Your example, I propose, *happens to work* but we are relying here on some very tenuous and poorly documented timing interactions between the callbacks of different objects! I'm happy for now ... but not very happy and I bet not forever!
Hey,
I recreated your example and onItemReady worked for me. Here’s my code:
$w.onReady(() => { $w('#myRepeater').onItemReady(($wInRepeater, itemData) => { const thereIsNoUrl = !itemData.url if (thereIsNoUrl) $wInRepeater('#myRepeaterButton').collapse() }) });
I used collapse instead of hide. The difference is that hide keeps the space taken by the button. This is probably not what you want to achieve.
Did you try adding a console.log(itemData) to onItemReady?
When you add a new row, it has no keys (except system ones like _id) defined. It’s like creating an empty object in pure JavaScript. In this case your check won’t work:
itemData.url == "" // false because undefined is not equal empty string
You redefine $w in the scope so that it doesn’t refer to the global $w:
($w, itemData, index) => { if(itemData.url=="") { $w("#myRepeaterButton").hide(); } }
In my example, I gave it a different name to make this distinction clearer.
Check out my example and see if it works for you.