Search code examples
javascriptjquerypromisecallbackbootbox

How to prevent closing bootbox while using async function?


What I'm trying to get is if an user gets any validation error, then bootbox will show that "this is required". Till now, I've achieved this. But the main problem is that - it closes the window of boot box if the user clicks "Yes" button. I'm getting this because I had to use g async callback in bootbox. For this reason, even after returning false, the bootbox is closing. But I want users to show the box until they press the cancel button. If they click Yes, then it should show the validation, with the box opened. This is my code:

bootbox.confirm({
    message: 'Test',
    buttons: {
        confirm: {
            label: 'Yes',
            className: 'btn-primary'
        },
        cancel: {
            label: 'No',
            className: 'btn-danger'
        }
    },
    callback: async function (result) {
        var isValid = $('#form').valid();
        if (result && !isValid) {
            return false; //it's not working. It's closing the bootbox
        }
        if (result && isValid) {
            /* for this await function I had to use async callback,
               without this bootbox is opend
            */
            var data = await self.createData();
            $.ajax({
                type: "POST",
                success: function (result) {

                },
            }).then(function () {
            });
        }
    }
});

How can I resolve this?


Solution

  • It does not appear to me that bootbox.confirm() has any sort of support for async callbacks like you are trying to use. The bootbox closes when your callback returns which will be at the point you hit your first await unless you explicitly return false, but an async callback function ALWAYS returns a promise which is not explicitly false. You cannot change that.

    What you can do is make your callback a regular callback function, not async that can return false if validation fails and then create an embedded async function where you can use await that you call from within that first callback like is shown below. Note that the bootbox will close before your asynchronous code completes so if there are any errors in the bootbox code, you will need to new way to present those errors, perhaps putting up a new bootbox. Here's one way to do this code while still using await.

    bootbox.confirm({
        message: 'Test',
        buttons: {
            confirm: {
                label: 'Yes',
                className: 'btn-primary'
            },
            cancel: {
                label: 'No',
                className: 'btn-danger'
            }
        },
        callback: function (result) {
            var isValid = $('#form').valid();
            if (result) {
                if (!isValid) {
                    // keep prompt open until user presses Cancel
                    return false;
                }
    
                async function run() {
                    const data = await self.createData();
                    const result = await $.ajax({ ... });
                    // do something with result
                }
                // now call async function here (dialog will close)
                run().catch(err => {
                    // do something with an error here
                    console.log(err);
                });
            }
            return true;
        }
    });
    

    Alternatively, you could avoid using await and only use .then() and .catch() and then you wouldn't need this extra layer of function:

    bootbox.confirm({
        message: 'Test',
        buttons: {
            confirm: {
                label: 'Yes',
                className: 'btn-primary'
            },
            cancel: {
                label: 'No',
                className: 'btn-danger'
            }
        },
        callback: function (result) {
            var isValid = $('#form').valid();
            if (result) {
                if (!isValid) {
                    // keep prompt open until user presses Cancel
                    return false;
                }
    
                self.createData().then(data => {
                    return $.ajax({ ... }).then(result => {
                        // do something with result
                    });
                }).catch(err => {
                    // do something with error here
                    console.log(err);
                });
            }
            return true;
        }
    });