forms - Delete confirmation for items within a modal - User Experience Stack Exchange
Delete data with confirmation modal - javascript
interaction design - modal inside a modal (delete confirmation) - User Experience Stack Exchange
Simple Bootstrap 4 Modal Delete Confirm - General Web Dev - SitePoint Forums | Web Development & Design Community
Videos
When you click on a task in my app it brings up a details modal showing all the info about the given task. From within the details modal I want to be able to delete the task, and I want to ask for confirmation when I do. I can't find a great way to handle this, a secondary modal popping up, overlay the original modal, a popout from the button, etc.
Trello uses the popout design you can see below, it works alright but doesn't feel right to me for some reason so I'm looking for other options hopefully with an example if possible!
I see several ways to solve this problem:
I agree with Izquierdo you should not design a table inside a pop-up window. But whatever the reason you have to do that please follow the next options.
Design it as you described earlier and then test it (the simple one). If users are frustrated consider another interaction. It shouldn't be real code implementation, a simple Figma prototype would be enough. Can be useful here: Framework to Evaluate and Improve the User Experience.
Design it as a nonmodal popup. Which appears as a dialog above the popup, but it won't be one more popup on top of the previous one. More about that Nonmodal popups
Do not ask for confirmation of deleting, but provide undo option. This decision should be considered with the front-end and backend teams because it solves the UX problem but can cost too much.
Dialog box on top of a modal should be avoided. From what you have described (i.e that you can't change the structure of your page and modal windows at the moment) I concur with Roman and Izquierdo that "Undo" option would be the best way to go in this situation of yours:

The problem is that your modal is outside of your for loop and that you are trying to access the $user variable in your modal. At this point in time the $user variable contains the last element of your for loop, because your loop has already iterated. That is the reason why you are always deleting your last user.
There are 2 options:
- Put your Modal code into your loop (this will create a modal for each entry)
- It is good practice to only have one modal and change the values dinamically (like you wanted to do). But for that you will have to pass/build the correct delete URL in your on click method
Give your form an id like id="delete-form" afterwards in your onclick method you can set the action dinamically:
$('#delete-form').attr('action', '/users/' + delete_id);
First, you did not name data-target="#exampleModal" it correctly. You must also enter the value of $user->id. Change it to data-target="#exampleModal{{ $user->id }}". Like this:
<a href="{{ '#' }}" class="delete-modal badge btn-danger" data-value="{{ $user->id }}" data-toggle="modal" data-target="#exampleModal{{ $user->id }}">
Secondly, You should put modal code in loop:
@foreach ($users as $user)
<div class="modal fade" id="exampleModal{{ $user->id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content shadow-sm">
<form action="/users/{{ $user->id }}" method="POST" class="d-inline">
@method('delete')
@csrf
<div class="modal-body">
<h3 class="text-center">Are you sure?</h3>
</div>
<div class="modal-footer justify-content-around pt-0 border-top-0">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger" name="delete_user">Delete</button>
</div>
</form>
</div>
</div>
</div>
@endforeach
Also pay attention to id tag: id="exampleModal{{ $user->id }}"
GET recipe
For this task you can use already available plugins and bootstrap extensions. Or you can make your own confirmation popup with just 3 lines of code. Check it out.
Say we have this links (note data-href instead of href) or buttons that we want to have delete confirmation for:
Copy<a href="#" data-href="delete.php?id=23" data-toggle="modal" data-target="#confirm-delete">Delete record #23</a>
<button class="btn btn-default" data-href="/delete.php?id=54" data-toggle="modal" data-target="#confirm-delete">
Delete record #54
</button>
Here #confirm-delete points to a modal popup div in your HTML. It should have an "OK" button configured like this:
Copy<div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
...
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<a class="btn btn-danger btn-ok">Delete</a>
</div>
</div>
</div>
</div>
Now you only need this little javascript to make a delete action confirmable:
Copy$('#confirm-delete').on('show.bs.modal', function(e) {
$(this).find('.btn-ok').attr('href', $(e.relatedTarget).data('href'));
});
So on show.bs.modal event delete button href is set to URL with corresponding record id.
Demo: http://plnkr.co/edit/NePR0BQf3VmKtuMmhVR7?p=preview
POST recipe
I realize that in some cases there might be needed to perform POST or DELETE request rather then GET. It it still pretty simple without too much code. Take a look at the demo below with this approach:
Copy// Bind click to OK button within popup
$('#confirm-delete').on('click', '.btn-ok', function(e) {
var $modalDiv = $(e.delegateTarget);
var id = $(this).data('recordId');
$modalDiv.addClass('loading');
$.post('/api/record/' + id).then(function() {
$modalDiv.modal('hide').removeClass('loading');
});
});
// Bind to modal opening to set necessary data properties to be used to make request
$('#confirm-delete').on('show.bs.modal', function(e) {
var data = $(e.relatedTarget).data();
$('.title', this).text(data.recordTitle);
$('.btn-ok', this).data('recordId', data.recordId);
});
Show code snippet
Copy// Bind click to OK button within popup
$('#confirm-delete').on('click', '.btn-ok', function(e) {
var $modalDiv = $(e.delegateTarget);
var id = $(this).data('recordId');
$modalDiv.addClass('loading');
setTimeout(function() {
$modalDiv.modal('hide').removeClass('loading');
}, 1000);
// In reality would be something like this
// $modalDiv.addClass('loading');
// $.post('/api/record/' + id).then(function() {
// $modalDiv.modal('hide').removeClass('loading');
// });
});
// Bind to modal opening to set necessary data properties to be used to make request
$('#confirm-delete').on('show.bs.modal', function(e) {
var data = $(e.relatedTarget).data();
$('.title', this).text(data.recordTitle);
$('.btn-ok', this).data('recordId', data.recordId);
});
Copy.modal.loading .modal-content:before {
content: 'Loading...';
text-align: center;
line-height: 155px;
font-size: 20px;
background: rgba(0, 0, 0, .8);
position: absolute;
top: 55px;
bottom: 0;
left: 0;
right: 0;
color: #EEE;
z-index: 1000;
}
Copy<script data-require="jquery@*" data-semver="2.0.3" src="//code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="bootstrap@*" data-semver="3.1.1" src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<link data-require="bootstrap-css@3.1.1" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" 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="myModalLabel">Confirm Delete</h4>
</div>
<div class="modal-body">
<p>You are about to delete <b><i class="title"></i></b> record, this procedure is irreversible.</p>
<p>Do you want to proceed?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok">Delete</button>
</div>
</div>
</div>
</div>
<a href="#" data-record-id="23" data-record-title="The first one" data-toggle="modal" data-target="#confirm-delete">
Delete "The first one", #23
</a>
<br />
<button class="btn btn-default" data-record-id="54" data-record-title="Something cool" data-toggle="modal" data-target="#confirm-delete">
Delete "Something cool", #54
</button>
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Demo: http://plnkr.co/edit/V4GUuSueuuxiGr4L9LmG?p=preview
Bootstrap 2.3
Here is an original version of the code I made when I was answering this question for Bootstrap 2.3 modal.
Copy$('#modal').on('show', function() {
var id = $(this).data('id'),
removeBtn = $(this).find('.danger');
removeBtn.attr('href', removeBtn.attr('href').replace(/(&|\?)ref=\d*/, '$1ref=' + id));
});
Demo: http://jsfiddle.net/MjmVr/1595/
http://bootboxjs.com/ - latest works with Bootstrap 3.0.0
The simplest possible example:
Copybootbox.alert("Hello world!");
From the site:
The library exposes three methods designed to mimic their native JavaScript equivalents. Their exact method signatures are flexible as each can take various parameters to customise labels and specify defaults, but they are most commonly called like so:
Copybootbox.alert(message, callback)
bootbox.prompt(message, callback)
bootbox.confirm(message, callback)
Here's a snippet of it in action (click "Run code snippet" below):
Copy$(function() {
bootbox.alert("Hello world!");
});
Copy<!-- required includes -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet"/>
<!-- bootbox.js at 4.4.0 -->
<script src="https://rawgit.com/makeusabrew/bootbox/f3a04a57877cab071738de558581fbc91812dce9/bootbox.js"></script>
Run code snippetEdit code snippet Hide Results Copy to answer Expand