Fomantic-UI
Fomantic-UI copied to clipboard
[checkbox] event beforeChecked, be able to use a Promise<boolean> instead of a boolean to prevent default behavior.
Feature Request
Hello, a nice feature would be the possibility to return a promise
That way, it would be possible for exemple to defer the resolution and waiting for an user input.
The scenario I have in mind is to open a modal asking for confirmation before choosing to check or uncheck the checkbox.
Example (if possible)
if would look like this: https://jsfiddle.net/6rdw20vy/1/ (of course it not functional, it's to illustrate what kind of scenario I have in mind)
I found a workaround by using window.confirm (https://jsfiddle.net/6rdw20vy/3/), but it is not very fomantic-style friendly.
It would be nice to allow promises for all of these cancelable callbacks, not sure how much work that'd be though.
The best I've got currently is to return false
every time and set checked
if the modal is approved: https://jsfiddle.net/n5hcgwu7/
@GammaGames Yes it's a good workaround, I have ended with the same kind of solution. But it's not perfect because "set checked" does not trigger the next callbacks (like onChange), and we cant use "check" because it will cause an infinite loop (beforeChecked -> check -> beforeCheck -> ...), so if there is something that must have been done in the onChange callback, that must be called manually.
I would appreciate having this ability. I often use callbacks with fetch, and would like to keep the modal open if fetch gets a statusCode other than 200. I would like to await the fetch, and return accordingly. However, the callback function has to be marked as async in order to do this.
Briefly looking at the code behind callbacks, we could add await to the callback call, like this from here:
if (ignoreRepeatedEvents || await settings.onApprove.call(element, $(this)) === false) {
The function that line is in would also need to be marked as async. Not sure where that gets called to know the problems associated it. I wouldn't mind putting together a PR that does this, but I would need to know first what negative effects this may result in.
This is something that i'm willing to implement too, but since the async
keyword will not work on IE11 🤔
However, I found an alternative that should work on all browsers. By checking the constructor name, we can determine if the callback is an async
function, then we use the Promise
system, or else, the good ol' synchronous call. All further code is stored in a callback that is applied when the callback is called.
For example in the modal onApprove
code:
event: {
approve: function() {
// our callback called after a promise or a classic call
var onApproveCallback = function (result) {
if (result === false) {
module.verbose('Approve callback returned false cancelling hide');
return;
}
module.hide(function() {
ignoreRepeatedEvents = false;
});
};
if(ignoreRepeatedEvents) {
return;
}
ignoreRepeatedEvents = true;
// here's the magic, we check if the constructor of our onApprove function
// is Async. If it's the case, we use the .then() function to call the callback after
// else our callback is called right now
if (settings.onApprove.constructor.name == 'AsyncFunction') {
settings.onApprove.call(element, $(this)).then(function (resolve) { onApproveCallback(resolve); })
} else {
onApproveCallback(settings.onApprove.call(element, $(this)))
}
},
...
}
So we can now use two ways to control the modal closing on approval:
- The old fashioned way, synchronous:
$('.ui.modal').modal({
onApprove: function($element) {
console.log('onApprove');
return true
}
});
- The new cool way, asynchronous backed:
$('.ui.modal').modal({
onApprove: async function($element) {
console.log('onApprove');
return new Promise((resolve) => {
// mocked async call
// modal will close is 3 seconds
setTimeout(function () { resolve(true) }, 3000)
})
},
});
Some code will need extra work to do (in checkboxes for example), but it's doable. What do you guys think about it ?
Very nice approach! Regardless of ie11, we always need to check if the call is async to decide how to call the callback . If we would always assume an async function, your .then
call will break on sync functions.
As we have many callbacks in many modules, we should think about making this a bit more generic (?)
Async functions will always have AsyncFunction
in their constructor.name
, but yeah, we also need to check the existence of a provided .then()
method to be sure.