DataTables not initialising correctly with Bootstrap modals - javascript

I'm having two problems with DataTables and Bootstrap modals.
Can't use dynamically built buttons after page 1
(Solved by Yuri)
I have this table, It is built dynamically by an AJAX request, the data loads correctly and builds up the table as it should, the buttons 'Manage' and 'Delete' have classes set on them, when clicked it should open another modal on top of the current one, on the 1st page of the table it works fine, if I go to page 2 it doesn't action anything as if there is no listener on it.
Datatables not initialising on the 2nd modal (Could be related to issue 1)
I have the below jQuery to initialise another DataTables on the modal that the 'Manage' button should open up. When it does open it should initialise another DataTables and get data from the AJAX request but in my network tab in Chrome developer tools it isn't being ran.
I have discovered the problem is, the DataTables isn't being initialized for some reason? the jQuery works up to there
I can't understand what I'm doing wrong here?
$(document).ready(function(){
$('.valid-tags').on('click', '.manageAutofixes', function(){
$('.autofixes-modal').modal('show');
$('.autofixes-modal').on('shown.bs.modal', function () {
var table = $('.autofixes-table').DataTable({
"ajax": {
"url": "/ajax/getValidTags.php",
"type": "POST",
"data": {
ruleID: ruleID,
type: 'autofixes'
},
},
"columnDefs": [{
"targets": 2,
"render": function(data, type, full, meta){
return '<button class="btn btn-default btn-sm manageAutofixes" type="button">Manage</button> <button class="btn btn-danger btn-sm deleteValid">Delete</button>';
}
}],
destroy: true
});
})
})
});
My modals HTML (included on the page)
<div class="modal fade autofixes-modal" tabindex="-1" role="dialog" aria-labelledby="LargeModalLabel" aria-hidden="true" data-keyboard="false" data-backdrop="static">
<div class="modal-dialog modal-wide">
<div class="modal-content">
<div class="modal-header modal-header-custom">
<input class="ruleID" type="hidden"></input>
<button type="button" class="close destroyTable" data-dismiss="modal" aria-hidden="true">x</button>
<h4 class="modal-title">Autofixes</h4>
</div>
<div class="modal-body">
<div class="topBar">
<div>
<input type="text" class="autofix inputTextStyling">
</div>
</div>
<hr>
<table class="table table-striped table-condensed autofixes-table" style="width:100%;">
<thead>
<tr>
<th>Autofix</th>
<th>Manage</th>
</tr>
</thead>
<tbody class="autoFixesTable">
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default destroyTable" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

To solve you buttons' problem, just use this kind of delegation:
$('#datatable').on('click', '.btn_class', function(){...});
It will match any click event occurring inside #datatable container on .btn_class elements, and it will solve your issue for dinamically added elements.
For what concerns Datatable inside your modal, I usually solve this by using a remote modal. That means that you put both your <div class="modal-body">..</div> html code and the relative JS code (that code to init the Datatable) on a (php) file, and load it with:
$("#myModal").on("show.bs.modal", function(e) {
var link = $(e.relatedTarget);
$(this).find(".modal-body").load(link.attr("href"));
});
EDIT: Remember to use $(document).ready(); in your remote php file too, so that code will run only when modal-body is fully loaded

The problem is that the buttons for the page 2 are loaded after you bind the action "click". You have to re-bind the action each time you load the results(by ajax)
you can use the datatable option fnDrawCallback
fnDrawCallback: function( oSettings ) {
$('.manageAutofixes').on('click', function(){
$('.autofixes-modal').modal('show');
alert('hi');
}
}

Related

Close Bootstrap Modal from within injected HTML?

Script to Call Modal, and HTML Skeleton for Modal: (which is working)
<script>
$(document).on("click", ".addworker", function (e) {
e.preventDefault();
var $popup = $("#popup");
var popup_url = $(this).data("popup-url");
$(".modal-content", $popup).load(popup_url, function () {
$popup.modal("show");
});
});
</script>
<div id="popup" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
</div>
</div>
</div>
The HTML loaded into modal-content is returned by a django view which sits behind the url. (this part is working too)
Inside this HTML i have the Button, which i want to use to then close the modal again.
But when the Modal got injected with the HTML and is open, it seems like the modal is out of scope, so i tried to get it back with var $popup = $("#popup");.
And i indeed get back the Object, but the $popup.modal("hide"); Method doesnt work.
<script>
$(document).on("click", ".cancel", function (e) {
var $popup = $("#popup");
$popup.modal("hide");
});
</script>
<div class="modal-header">
<h1>{{ aoe }} Worker: {{ id }}</h1>
</div>
<form action="add/" method="post">
{% csrf_token %}
<div class="modal-body">
{{ form|crispy }}
</div>
<div class="modal-footer">
<input type="button" class="btn btn-secondary cancel" value="Cancel">
<input type="submit" class="btn btn-primary" value="Submit">
</div>
</form>
My first workaround: (inside the html file that gets injected to the modal)
<script>
$(document).on("click", ".cancel", function (e) {
var $popup = document.querySelector('#popup');
$popup.classList.remove('show');
$('#popup').css("display", "none");
var $back = document.querySelector('.modal-backdrop');
$back.parentNode.removeChild($back);
});
</script>
This works, but is not really that convenient.
Second Workaround:
Just call $("#popup").click() when pressing the Close Button,
to simulate a click into the free area next to the Modal.
Feels like cheating, but at least its less effort now.
Still, i would like to know the "proper" way to do this.
Problem Solved. I included JQuery in Both HTML Files, which seemed to cause the function not to work anymore. Now it works using $("#popup").modal('hide');

How do you get a javascript function to recognize html code returned by an ajax request

I apologize ahead of time if this question has been addressed...I looked and did not see one. I am building a web app using flask. After successfully logging in the user is routed to index.html via the following code:
#blueprint.route('/index')
#login_required
def index():
return render_template('home/index.html', segment='index')
My index.html includes an ajax request which builds and returns custom html such as:
<div class="card" style="max-width: 22rem;">
<!-- Card content -->
<div class="card-body pb-3">
<!-- Title -->
<h4 class="card-title font-weight-bold">TailNumber</h4>
<h6 class="card-subtitle text-muted d-flex justify-content-between">
<p>AircraftType</p>
<p>Operator</p>
</h6>
<div class="collapse-content">
<div class="collapse" id="tail_number" style="">
<table class="table table-borderless table-sm mb-0">
<tbody>
<tr>
<td class="font-weight-normal align-middle">Engine</td>
<td class="float-right font-weight-normal">
<p class="mb-1">2.5<span class="text-muted">IPS</span></p>
</td>
</tr>
</tbody>
</table>
</div>
<hr class="">
<button class="btn" type="button" data-toggle="collapse" data-target="#tail_number"
aria-expanded="false" aria-pressed="false" aria-controls="tail_number">Show
</button>
</div>
</div>
</div>
I also include a javascript function designed to change the text of my "collapse" button from "Show" to "Hide" and back again as the content is expanded and collapsed:
$(document).ready(function() {
$('[data-toggle="collapse"]').click(function() {
$(this).toggleClass("active");
if ($(this).hasClass("active")) {
$(this).text("Hide");
} else {
$(this).text("Show");
}
});
});
While the collapse feature of my html code works the text is not being updated. If I hard code the html instead of loading it via my ajax request then the button text changes as advertised. My assumption is that the javascript above is not being applied to the html returned by the ajax request but I am not sure how to verify this or, more importantly, correct it. My simplified ajax request is here:
<script>
$(function(){
function update_fleet_view(){
// create an ajax request
var req = new XMLHttpRequest();
req.open("GET", "/ajax", true);
req.send();
req.onload = function () {
// update page with new fleet view
var data = JSON.parse(req.responseText);
document.getElementById("fleet_view").innerHTML = data["fleet_view"];
// timeout to run again after 1 minute
setTimeout(update_fleet_view, 60000);
}
}
// call the function to get it started
update_fleet_view();
})();
</script>
Event delegation was exactly what I needed...thanks for the link James.
I ended up with the following modifications to my JS:
$(document).ready(function() {
$("#fleet_view").on("click", '[data-toggle="collapse"]', function() {
if ($(this).hasClass("collapsed")) {
$(this).text("Hide");
} else {
$(this).text("Show");
}
});
});
where "fleet_view" is the "id" that is referenced by my ajax script.
I attempted to leave the $(this).toggleClass("active") in the script however I could never get it to toggle. Rather than spending precious time tracking that issue down I decided to look at the "collapsed" field as the click event toggles that automatically. I also had to modify my button to add the "collapsed" class otherwise the button text would not update for the first round of clicks (see code below). The "if/else" statement seems backwards but I assure you it works as intended. Again, I can only assume that the "collapsed" class gets added or removed after this script executes.
<button class="btn collapsed" type="button" data-toggle="collapse" data-target="#tail_number"
aria-expanded="false" aria-pressed="false" aria-controls="tail_number">Show
</button>

Bootstrap 4 load modal-Content from other page [duplicate]

I can't make the Modal work in the remote mode with the new Twitter Bootstrap release : Bootstrap 4 alpha. It works perfectly fine with Bootstrap 3. With bootstrap 4 I am getting the popup window, but the model body is not getting loaded. There is no remote call being made to myRemoteURL.do to load the model body.
Code:
<button type="button" data-toggle="modal" data-remote="myRemoteURL.do" data-target="#myModel">Open Model</button>
<!-- Model -->
<div class="modal fade" id="myModel" 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-label="Close">
<span aria-hidden="true">×</span>
</button>
<h3 class="modal-title" id="myModalLabel">Model Title</h3>
</div>
<div class="modal-body">
<p>
<img alt="loading" src="resources/img/ajax-loader.gif">
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
Found the problem: They have removed the remote option in bootstrap 4
remote : This option is deprecated since v3.3.0 and will be removed in v4. We recommend instead using client-side templating or a data binding framework, or calling jQuery.load yourself.
I used JQuery to implement this removed feature.
$('body').on('click', '[data-toggle="modal"]', function(){
$($(this).data("target")+' .modal-body').load($(this).data("remote"));
});
According to official documentation, we can do the follow(https://getbootstrap.com/docs/4.1/components/modal):
$('#exampleModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget) // Button that triggered the modal
var recipient = button.data('whatever') // Extract info from data-* attributes
// If necessary, you could initiate an AJAX request here (and then do the updating in a callback).
// Update the modal's content. We'll use jQuery here, but you could use a data binding library or other methods instead.
var modal = $(this)
modal.find('.modal-title').text('New message to ' + recipient)
modal.find('.modal-body input').val(recipient)
})
So, I believe this is the best approach (works for BS 5 too):
<!-- Button trigger modal -->
<a data-bs-toggle="modal" data-bs-target="#modal_frame" href="/otherpage/goes-here">link</a>
<!-- Modal -->
<div class="modal fade" id="modal_frame" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<!-- Completes the modal component here -->
</div>
<script>
$('#modal_frame').on('show.bs.modal', function (e) {
$(this).find('.modal-body').load(e.relatedTarget.href);
});
</script>
e.relatedTarget is the anchor() that triggers the modal.
Adapt to your needs
As some of the other answers and Bootstrap docs indicate, Bootstrap 4 requires handling the show.bs.modal event to load content into the modal. This can be used to either load content from an HTML string, or from a remote url. Here's a working example...
$('#theModal').on('show.bs.modal', function (e) {
var button = $(e.relatedTarget);
var modal = $(this);
// load content from HTML string
//modal.find('.modal-body').html("Nice modal body baby...");
// or, load content from value of data-remote url
modal.find('.modal-body').load(button.data("remote"));
});
Bootstrap 4 Remote URL Demo
Another option is to open the modal once data is returned from an Ajax call...
$.ajax({
url: "http://someapiurl",
dataType: 'json',
success: function(res) {
// get the ajax response data
var data = res.body;
// update modal content
$('.modal-body').text(data.someval);
// show modal
$('#myModal').modal('show');
},
error:function(request, status, error) {
console.log("ajax call went wrong:" + request.responseText);
}
});
Bootstrap 4 Load Modal from Ajax Demo
In Asp.NET MVC, this works for me
html
Edit item
<div class="modal" id="modalPartialView" />
jquery
<script type="text/javascript">
function Edit(id)
{
$.ajax({
url: "#Url.Action("ActionName","ControllerName")",
type: 'GET',
cache: false,
data: {id: id},
}).done(function(result){
$('#modalPartialView').html(result)
$('#modalPartialView').modal('show') //part of bootstrap.min.js
});
}
<script>
Action
public PartialViewResult ActionName(int id)
{
// var model = ...
return PartialView("_Modal", model);
}
If you use the Jquery slim version (as in all the Bootstrap 4 docs and examples) the load function will fail
You need to use the full version of Jquery
This process is loading the current dynamic. data remote = "remoteContent.html"
<!-- Link trigger modal -->
<a href="javascript:void(0);" data-remote="remoteContent.html" data-toggle="modal" data-target="#myModal" data-remote="true" class="btn btn-default">
Launch Modal
</a>
This trick : data-remote="remoteContent.html"
<!-- Default bootstrap modal example -->
<div class="modal fade" id="myModal" 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-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>

How to bind a Bootstrap modal to action buttons rendered with Bootstrap Table

I'd like to use a Bootstrap modal triggered by the action buttons I have added to my Bootstrap Table, but I can't figure it out. Is there any way to bind the data attributes of these buttons to the modal? It's not working in this case. Or in case I can only do it from javascript, how could I do it, since it's not working either in my case. No error thrown either way.
This is a screen capture to make clear which buttons I'm talking about: the edit and remove buttons on each row.
And the code I'm using:
// Click handler for the edit button when I try the data attributes way
function operateFormatter(value, row, index) {
return [
'<a class="edit ml10" href="#dialogo-destinos" data-toggle="modal" title="Edit">',
'<i class="glyphicon glyphicon-edit"></i>',
'</a>',
'<a class="remove ml10" href="javascript:void(0)" title="Remove">',
'<i class="glyphicon glyphicon-remove"></i>',
'</a>'
].join('');
}
// Code for the javascript way:
window.operateEventsDestinos = {
'click .edit': function (e, value, row, index) {
var $dialogo = $('.dialogo.destinos');
... // actions to populate the modal according to the row data
$dialogo.modal('show');
...
},
'click .remove': function (e, value, row, index) {
...
}
};
The table rendered by Bootstrap Table:
<div class="row">
<div class="col-md-12">
<table id="tabla-datos-destinos" class="bootstrap-table table table-striped table-hover"
data-pagination="true" data-search="true" data-show-header="true"
data-show-columns="true" data-show-refresh="true" data-toolbar="#custom-toolbar"
data-show-toggle="true" data-show-export="true">
<thead>
<tr>
<th data-field="nombre" data-sortable="true">Nombre</th>
<th data-field="poblacion" data-sortable="true">Localidad</th>
<th data-field="provincia" data-sortable="true">Provincia</th>
<th data-field="operate" data-formatter="operateFormatter" data-events="operateEventsDestinos">Acciones</th>
</tr>
</thead>
</table>
</div>
</div>
And of course the HTML for the modal:
<div class="dialogo destinos modal fade" id="dialogo-destinos">
...
</div>
Any ideas why this isn't working or what should I do?
EDIT:
I've also tried using a button instead of an A tag, but I can't get it working either. It's instead running the default behavior: submitting the form. Is there any way to add the action buttons directly within the HTML of the table instead of having to add them through javascript, which I think is the reason why the binding is not done?
You can simply tie data-toggle and data-target to a button inside your acciones div.
For example:
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#dialogo-destinos">
Launch Modal
</button>

jquery modal: closing a modal and then having it available as a clickable option

I been scratching my head on this one for a while.
Writing a plugin in grails that calls on bootstrap-mini.js and most of its css. Everything works fine. The issue I am having is I have a remote form which onComplete runs java script:
https://github.com/vahidhedayati/ml-test/blob/master/grails-app/views/mailingListModal/_modalcreate.gsp
<g:javascript>
function ${controller}CloseModal() {
var myClone=$('#BuildModal${id}').clone();
$('#BuildModal${id}').dialog().dialog('close');
$(".modal-backdrop").hide();
$('body').removeClass('modal-open');
//var myCloner = myClone.clone();
$('#${divId}1').hide().append(myClone);
//$('body').append(myClone);
<g:if test="${!disablecheck.equals('true') }">
var controller="${controller }";
var divId="${divId }";
$.get('${createLink(controller:"MailingListEmail", action: "getAjaxCall")}?ccontroller='+controller+'&divId='+divId,function(data){
$('#${divId}').hide().html(data).fadeIn('slow');
});
</g:if>
}
</g:javascript>
The bits at the top of the function is all the things I have tried so far.
https://github.com/vahidhedayati/ml-test/blob/master/grails-app/views/mailingListEmail/contactclients.gsp
<div class="tbutton">
<button href="#BuildModalSENDERS" class="btn btn-block btn-success" role="button" data-toggle="modal" title="Configure New Sender">
New Sender?</button>
<div id="mailerSenders1">
<g:render template="/mailingListModal/modalcreate" model="[title:'Add Senders Email', controller: 'mailingListSenders', callPage: 'form' , divId: 'mailerSenders', id: 'SENDERS' ]" />
</div>
And finally modelForm which is included on the top of the modalcreate.gsp (now shown)
<div class="modal fade" id="BuildModal${id}" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<g:formRemote id="${controller}" name="urlParams" class="form-horizontal" url="[controller:controller, action:'save']"
update="BuildModal${id}" onComplete="${controller}CloseModal()"
>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">x</button>
<h3>${title }</h3>
</div>
<div class="modal-body">
<div class="form-group">
<g:render template="/${controller }/${callPage }"/>
<g:submitToRemote class="myformsubmit" url="[controller:controller, action:'save']" update="BuildModal${id}" onComplete="${controller}CloseModal()" value="Create" />
</div>
</div>
</g:formRemote>
</div>
</div>
</div>
</div>
So it is a remote Form that submits can calls the above CloseModal
The question is when I Close this how do I make it available again when the user clicks the button to create new email ?
After adding all the cloning at the top of java script the only difference I was able to make was to make it display the backdrop on 2nd click so it went black on 2nd click but did not show up the modal content.
ok got it working by doing this:
<button href="#BuildModalSENDERS" class="btn btn-block btn-success" role="button" data-toggle="modal" title="Configure New Sender"
Now adding
onClick="runCheck()"> .....
<g:javascript>
function runCheck() {
$('#mailerSenders1').show();
}
</g:javascript>
That seems to work fine, it now loads up the page, just to add when I did body it just loaded it up again from the commented out attempts, modal.hide etc did not work and the hide attempts just showed it up under some other layers of the same page.. anyways this works fine now. sorry

Categories