Reputation: 93
I have a Bootstrap modal dialog, which when Yes is selected returns a resolved promise. Upon the promise being resolved, the modal should be displayed again. The line to display the modal is hit, but the modal does not display. What am I doing wrong?
$(function() {
showModalDialog('Confirm1', "Select Yes or No", 'Yes', 'No')
.done(function() {
alert('You selected Yes once!');
showModalDialog('Confirm2', "Select Yes or No", 'Yes', 'No')
.done(function() {
alert('You selected Yes twice!');
});
});
});
function showModalDialog(title, message, button1Caption, button2Caption) {
var deferred = $.Deferred();
$('#modalTitle').html(title);
$('#modalMessage').html(message);
$('#modalButton1').html(button1Caption);
$('#modalButton2').html(button2Caption);
$('#modalButton1').one('click', function() {
deferred.resolve();
});
$('#modalButton2').one('click', function() {
deferred.reject();
});
$('#modalDialog').one('hidden.bs.modal', function() {
//remove the handler for the button in case it was never invoked, otherwise it will
//still be there the next time the dialog is shown
$('#modalButton1').off('click');
deferred.reject();
})
$('#modalDialog').modal();
return deferred.promise();
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="modal fade" id="modalDialog" tabindex="-1" role="dialog" aria-labelledby="modalTitle" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<!--<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>-->
<h4 class="modal-title" id="modalTitle"></h4>
</div>
<div class="modal-body" id="modalMessage"></div>
<div class="modal-footer">
<button type="button" id="modalButton1" class="btn btn-default" data-dismiss="modal"></button>
<button type="button" id="modalButton2" class="btn btn-default" data-dismiss="modal"></button>
</div>
</div>
</div>
</div>
Upvotes: 0
Views: 2168
Reputation: 14541
Blame the animations! :)
The animation of the hiding modal takes some finite time to complete. If you try to "show" the modal again during this time, it will not work. A simple workaround is to delay the "show" action slightly.
For example, delaying it by one second will work just fine:
setTimeout(function() {
showModalDialog('Confirm2', "Select Yes or No", 'Yes', 'No')
.done(function() {
alert('You selected Yes twice!');
});
}, 1000);
$(function() {
showModalDialog('Confirm1', "Select Yes or No", 'Yes', 'No')
.done(function() {
alert('You selected Yes once!');
setTimeout(function() {
showModalDialog('Confirm2', "Select Yes or No", 'Yes', 'No')
.done(function() {
alert('You selected Yes twice!');
});
}, 1000);
});
});
function showModalDialog(title, message, button1Caption, button2Caption) {
var deferred = $.Deferred();
$('#modalTitle').html(title);
$('#modalMessage').html(message);
$('#modalButton1').html(button1Caption);
$('#modalButton2').html(button2Caption);
$('#modalButton1').one('click', function() {
deferred.resolve();
});
$('#modalButton2').one('click', function() {
deferred.reject();
});
$('#modalDialog').one('hidden.bs.modal', function() {
//remove the handler for the button in case it was never invoked, otherwise it will
//still be there the next time the dialog is shown
$('#modalButton1').off('click');
deferred.reject();
})
$('#modalDialog').modal();
return deferred.promise();
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="modal fade" id="modalDialog" tabindex="-1" role="dialog" aria-labelledby="modalTitle" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<!--<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>-->
<h4 class="modal-title" id="modalTitle"></h4>
</div>
<div class="modal-body" id="modalMessage"></div>
<div class="modal-footer">
<button type="button" id="modalButton1" class="btn btn-default" data-dismiss="modal"></button>
<button type="button" id="modalButton2" class="btn btn-default" data-dismiss="modal"></button>
</div>
</div>
</div>
</div>
Note: I haven't checked the source code or experimented with these modals enough to know the minimum amount of delay required to make this work. But I don't mind a delay of one second anyway as it is barely noticeable.
As you suggest, waiting for Bootstrap's hidden.bs.modal
event is a better in this case. As described in the v3 Docs here:
This event is fired when the modal has finished being hidden from the user (will wait for CSS transitions to complete).
So moving the promise.resolved
to the event handler for hidden.bs.modal
would also do the trick.
To do that, I've added a variable modalResult
which keeps track of user's choice, and triggers deferred.resolve
or deferred.reject
accordingly.
$(function() {
showModalDialog('Confirm1', "Select Yes or No", 'Yes', 'No')
.done(function() {
alert('You selected Yes once!');
showModalDialog('Confirm2', "Select Yes or No", 'Yes', 'No')
.done(function() {
alert('You selected Yes twice!');
});
});
});
function showModalDialog(title, message, button1Caption, button2Caption) {
var modalResult = false;
var deferred = $.Deferred();
$('#modalTitle').html(title);
$('#modalMessage').html(message);
$('#modalButton1').html(button1Caption);
$('#modalButton2').html(button2Caption);
$('#modalButton1').one('click', function() {
// Wait for the modal to get hidden.
// deferred.resolve();
modalResult = true;
});
$('#modalButton2').one('click', function() {
// Wait for the modal to get hidden.
// deferred.reject();
modalResult = false;
});
$('#modalDialog').one('hidden.bs.modal', function() {
//remove the handler for the button in case it was never invoked, otherwise it will
//still be there the next time the dialog is shown
$('#modalButton1').off('click');
if(modalResult) {
deferred.resolve();
} else {
deferred.reject();
}
})
$('#modalDialog').modal();
return deferred.promise();
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="modal fade" id="modalDialog" tabindex="-1" role="dialog" aria-labelledby="modalTitle" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<!--<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>-->
<h4 class="modal-title" id="modalTitle"></h4>
</div>
<div class="modal-body" id="modalMessage"></div>
<div class="modal-footer">
<button type="button" id="modalButton1" class="btn btn-default" data-dismiss="modal"></button>
<button type="button" id="modalButton2" class="btn btn-default" data-dismiss="modal"></button>
</div>
</div>
</div>
</div>
Upvotes: 4