Can't shuffle/randomize order of repeater items as dataset forces its current sort onto repeater items

I’ve got a repeater that shows items in rows with 3 columns, continuing down the page with infinite scroll. The repeater items are connected to the dataset not by code but by the Wix Editor “Connect Repeater” menu.

I want to show a random order of the items when the page loads and when a “Shuffle” button is pressed. I’ve got code that works for shuffling and assigning the shuffled data to the dataset. However, it appears the dataset forces it’s current sort on the repeater items, thereby unshuffling the items and resorting back into Ascending/Descending etc.


So the issue is not the shuffling algorithm, but rather how do I assign the repeater data to the shuffled data without the dataset forcing its current sort back onto the repeater data and therefore losing the shuffled order?

$w.onReady(() => {
$w('#dataset').onReady(() => $w('#repeater').data = shuffle($w('#repeater').data));
})

function shuffle(arr) {
    for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
}

I was running some “clear filter” functions I wrote before calling the “shuffle function”. I figured out that applying a filter to the dataset, even it’s just a basic $w(‘#dataset’).setFilter(wixData.filter()) would cause it to reset to its default current sort.

It appears any code that interacts with the repeater dataset causes it to default back to its current sort. Even basic code for an infinite scroller like below

export function loadingStrip_viewportEnter ( event ) {
$w ( “#loadMore” ). show (); //show hidden loading icon
$w ( “#productsDataset” ). loadMore ()
. then (() => {
$w ( “#loadMore” ). hide (); //after more loaded, rehide loading icon
});
}

Any idea what is going on how I can get the random shuffle to “stick” despite applying filters and infinite scroll?

Always run the shuffle function after you change the dataset current contents. After the promises like .setFilter, .next, . previous, .refresh, .loadMore, etc… get fulfilled.

$w("#productsDataset").loadMore()
 .then(() => {
$w('#repeater').data = shuffle($w('#repeater').data));
})

Wouldn’t that rerun the shuffle algorithm then? For example, once entering the “load more” view port (for infinite scroll), the shuffle algorithm gets run again, causing the dataset to be shuffled every time you hit the end of “loaded items”.

The desired functionality would be for the shuffle to only occur upon website initialisation and when the “shuffle” button is pressed; after that, filters can be applied and the user can scroll howevermuch they want, but the “random sort” of the dataset should stay as is.

You’re right/
So do it like:

let data;
$w.onReady(() => {
$w('#dataset').onReady(() => {
data = shuffle($w('#repeater').data);
$w('#repeater').data = data;
});
})

function shuffle(arr) {
    for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
}
export function loadingStrip_viewportEnter(event) {
    $w("#loadMore").show();
    $w("#productsDataset").next()
	.then(() => {
	data = data.concat(shuffle($w('#repeater').data));
	$w('#repeater').data = data;
})
}

The above describes the code for case where you wish to connect the repeater to the dataset via the UI.
And I wouldn’t connect the repeater to the dataset thorough the UI at all but use:

let data = [];
$w.onReady(() => {
$w('#dataset').onReady(shuffleAndBind);
});
})

async function shuffleAndBind() {
const arr = await $w('#dataset').getItems(0,100)//use the number of items you present for one page instead of 100.
    for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    data = data.concat(arr);
	$w('#repeater').data = data;
	$w('#repeater').forEachItem(($i, iData) => {
	$i('#title').text = iData.title;
	//continue with binding the data to the repeater elements
	})
}
export function loadingStrip_viewportEnter(event) {
    $w("#loadMore").show(); 
    $w("#productsDataset").next()
	.then(shuffleAndBind)
}