Cancel a promise if focus leaves a result in a repeater

I have a repeater with multiple results. In each repeater is a textbox and a button.
When the button is clicked, an HTML iframe is called with a promise for the qr scan data. However, if while waiting for this message, the user clicks the button in another result in the repeater, then the Qr scan data that is returned fills in the textbox of both of the results.

Any suggestions to fix this?

$w("#RegRep").onItemReady(($item, itemData, index) => {
if (itemData.checkedIn) {
$item("#CheckinButton").label = "Re-Scan?";
$item("#CheckinButton").style.color = "rgb(209, 6, 6)";
$item("#CheckinButton").style.backgroundColor = "rgb(252, 223, 3)";
}
const repeatedButton = $item("#CheckinButton");
const bibInputField = $item("#bibInput");
bibInputField.onFocus((bibScan) => {
$w("#html2").postMessage("scan");
scannerWait = false;
$w("#html2").onMessage((bibResult) => {
scannerWait = true;
console.log(bibResult.data);
if (bibResult.data.length === 4) {
bibInputField.value = bibResult.data;
}
});
});
repeatedButton.onClick((event) => {
let bibNm = $item("#bibInput").value;
let checkinButton = $item("#CheckinButton"); //the button that is in each repeater entry
if (!bibNm) { //if the input box in the repeater is empty
$w("#html2").postMessage("scan"); //telling iframe is start the scanner
$item("#CheckinButton").label = "Scanning";
scannerWait = false;

$w("#html2").onMessage((bibResult) => { 

//this is the promise being waited on that is causing issues if the user clicks the checkin button on a different repeater entry.

scannerWait = true;
console.log(bibResult.data);
if (bibResult.data.length === 4) {
bibInputField.value = bibResult.data;
$item("#bibInput").enable();
$item("#CheckinButton").label = "Confirm";
}
});

} else {
checkDuplicate(bibNm)
.then((bibDuplicate) => {
console.log(bibDuplicate);
if (bibNm > 999 && bibNm <= 9999 && !itemData.checkedIn && !bibDuplicate) {
$item("#bibInput").disable();
itemData.bibNum = bibNm;
itemData.checkedIn = true;
$w("#2020RegDataset").save()
.then((item) => {
checkinButton.label = "Re-Scan?";
checkinButton.style.color = "rgb(209, 6, 6)";
checkinButton.style.backgroundColor = "rgb(252, 223, 3)";
refreshing();
})
.catch((err) => {
let errMsg = err;
});
} else if (bibDuplicate && bibNm) {
bibNm.enable();
bibNm.value = undefined;
$item("#CheckinButton").label = "Re-Scan";
$item("#CheckinButton").style.backgroundcolor = "rgb(255, 255, 255)";
$item("#CheckinButton").style.Color = "rgb(252, 223, 3)";
wixWindow.openLightbox("Duplicate Bib Error");
}

});
}
});
});

Your scannerWait approach won’t work if it’s only checking for the beginning of the event, not the completion. Maybe try some approach with an interval and break it when either a different button is clicked or when a message is received from the iframe.

Hi @skmedia ,
Thank you for the quick response.
So I have another promise question that’s part of this question.

I am attempting to stack .then however, I have tried many different ways and can’t seem to get it working. I cant get bast the first .then. I am assuming I have some return/resolve issues with the promise.
I am trying to get this order of operations:
onReady => then setFieldValues => then save the dataset => then change some button properties

below is the code of my most recent attempt. Any suggestions on what I have done wrong.
Thank you,

$w("#2020RegDataset").onReady()
.then(()=>$w("#2020RegDataset").setFieldValues({ "bibNum": bibNm, "checkedIn": true }))
.then(()=>$w("#2020RegDataset").save())
.then(() => {
checkinButton.label = "Debug3";
checkinButton.label = "Re-Scan?";
checkinButton.style.color = "rgb(209, 6, 6)";
checkinButton.style.backgroundColor = "rgb(252, 223, 3)";
refreshing();
});

The problem is the onReady event only fires once on dataset load, and it’s not a promise - it’s only a void callback. You can add event listeners that will fire inside of it for as long as the page is active, but it only happens once.

So:

$w('#dataset').onReady(() => {
    promise1().then(r => {
        promise2().then(res => {
        }).catch(err => console.error(err));
    }).catch(e => console.error(e));
});

@skmedia
so if i want these promises to happen every time a button is clicked, then i cant use onReady. Do i even need on ready if this whole statement is within the page onready statement?

@logan No, you don’t. The proper way is to wait until the dataset is ready to add your event listener, not to add an onready inside of the event listener itself.

@skmedia So it would be better if I wrapped the “repeater onReady” with the “dataset onReady” then?

@skmedia Also thank you for the help with promise stacking. Turns out however, that i dont think setFieldValues is async. so I ended up using the following code that worked:

$w("#2020RegDataset").setFieldValues({ "bibNum": bibNm, "checkedIn": true })
$w("#2020RegDataset").save().then(() => {
checkinButton.label = "Re-Scan?";
checkinButton.style.color = "rgb(209, 6, 6)";
checkinButton.style.backgroundColor = "rgb(252, 223, 3)";
refreshing();
}).catch(err => console.error(err));

@logan That looks good to me. Make sure to check the API docs to see if something is a promise or if it has a callback. The linter may or may not be consistent on this, so that’s the better option.

Also, if you needed to, you could either create your own promise for setfieldvalues or use async await .