javascriptbootbox

My bootbox.js confirm dialogue requires two clicks to save


In order to display two input fields in a bootbox.confirm box, I've embedded an HTML form in the message field. Everything works fine, but if I enter text in the textarea, the Save button loses focus, and two clicks on the save button are required to execute save and clear the modal. The problem is exterior to the code below, though. This jsfiddle functions just fine with one click. I can't practically share the thousands of lines of codes this sits in, anyone know what might be causing this and how I can fix it?

bootbox.confirm({
      title: "Save Foo",
      message: '<div class="row">  ' +
      '<div class="col-md-12"> ' +
      '<div class="text-center">Save</div>' +
      '<form class="form-horizontal"> ' +
      '<div class="form-group"> ' +
      '<label class="col-md-4 control-label" for="Question">Question</label> ' +
      '<div class="col-md-4"> ' +
      '<input id="name" name="name" type="text" value="Question" class="form-control input-md"> ' +
      '<span class="help-block">You can edit your question before saving</span> </div> ' +
      '</div> ' +
      '<div class="form-group"> ' +
      '<label class="col-md-4 control-label" for="notesbox">Notes:</label> ' +
      '<div class="col-md-4"> <div class="textarea"> <label for="notesbox"> ' +
      '<textarea name="notesbox" id="notesbox" rows="10" cols="30"></textarea></form></div> ' +
      '</label> ' +
      '</div>' +
      '</div> ' +
      '</div> </div>' +
      '</form> </div>  </div>',
    buttons: {
        'cancel': {
            label: 'Don\'t save',
            className: 'btn-danger pull-left'
        },
        'confirm': {
            label: 'Save',
            className: 'btn-success pull-right',
        }
    },callback: function (result) { if (result == true)
    { alert('Success')}
                  }
                }
              
        );

Solution

  • I'd start by using a script template, rather than using string concatenation to build your message - it would make it obvious that your current message has some invalid markup, which isn't doing you any favors. Here's one way of doing that:

    <script type="text/template" id="form-template">
        <div class="text-center">Save</div>
        <form class="form-horizontal">
            <div class="form-group">
                <label class="col-md-4 control-label" for="Question">Question</label>
                <div class="col-md-4">
                    <input id="name" name="name" type="text" value="Question" class="form-control input-md">
                    <span class="help-block">You can edit your question before saving</span> 
                </div>
            </div>
            <div class="form-group">
                <label class="col-md-4 control-label" for="notesbox">Notes:</label>
                <div class="col-md-4"> 
                    <div class="textarea"> 
                        <textarea class="form-control" name="notesbox" id="notesbox" rows="10" cols="30"></textarea>
                    </div>
                </div>
            </div>
        </form>
    </script>
    

    The type="text/template" attribute on the script tag means that your browser won't treat the contents of the tag as JavaScript to be executed. You can pretty much use just about anything there, but text/template conveys the meaning pretty well, so I stick with that.

    With that template tag, you can then get the message for your dialog by doing something like this:

    let message = $('#form-template').html();
    

    With that in place, I'd update your Bootbox usage to use the bootbox.dialog helper, rather than try to use bootbox.confirm for something it wasn't intended for. Here's your code, updated:

    let message = $('#form-template').html();
    
    let msgbox = bootbox.dialog({
        title: "Save Foo",
        message: message,
        buttons: {
            'cancel': {
                label: "Don't save",
                className: 'btn-danger pull-left'
            },
            'confirm': {
                label: 'Save',
                className: 'btn-success pull-right',    
                callback: function () {
                    let form = msgbox.find('form');
                    if(form.valid()){
                        alert('Valid!');
                        
                        msgbox.modal('hide');
                    }
                    
                    return false;
                }
            }
        }
    });
    

    When using the dialog helper, the global callback is no longer executed; rather, each button would have it's own callback, as shown. In this example, I have return false; as the last line, so that the modal will not close automatically. This lets me validate the form (here, I'm assuming jQuery Validate is in use) and whatever else I wanted to do (such as submit the form via AJAX, etc.). Then, we use Bootstrap's modal() function to dismiss our dialog.