Display loading sweet alert dialog while waiting ajax finnish - javascript

I'm using https://sweetalert2.github.io/
I tried using one single sweet dialog, but due to the others I couldn't make it work, and i've seen examples with fetch, but I'm using ajax, I saw a example with nested dialogs but once again, with fetch. In the code I put there are 2 comments where I would like to handle the loading dialog.
This is my script:
$(document).on("submit", "#formNuevoEstudiante", function (event) {
event.preventDefault();
$("#btnSubmit").prop("disabled", true);
let ap_paterno = $("#ap_paterno").val();
let ap_materno = $("#ap_materno").val();
let nombre = $("#nombre").val();
let sexo = $("#sexo option:selected").val();
let no_control = $("#no_control").val();
let carrera = $("#carrera option:selected").val();
let semestre = $("#semestre option:selected").val();
let sexo2 = sexo == "F" ? "Femenino" : "Masculino";
let mensaje = `x`;
let mensaje2 = `x`;
//Here is where I wan't to integrate the loading dialog
$.ajax({
url: "utils/ajax/nuevo_estudiante.php",
method: "POST",
data: {
ap_paterno: ap_paterno,
ap_materno: ap_materno,
nombre: nombre,
sexo: sexo,
no_control: no_control,
carrera: carrera,
semestre: semestre,
},
success: function (resp) {
if (resp == "existe") {
Swal.fire({
title: "Ya se encuentra registrado",
text: "x",
icon: "warning",
confirmButtonText: "Aceptar",
confirmButtonColor: "#0275D8",
}).then(function () {
window.location.href = "index2.php";
});
} else if (resp == "error") {
Swal.fire({
title: "Error",
text: "x",
icon: "error",
confirmButtonText: "Aceptar",
confirmButtonColor: "#0275D8",
});
} else if (resp == "ok") {
//Here is where I wan't to close the loading dialog
Swal.fire({
title: "Registro exitoso",
html: "<pre>" + mensaje + "</pre>" + mensaje2,
icon: "success",
confirmButtonText: "Aceptar",
confirmButtonColor: "#0275D8",
}).then(function () {
window.location.href = "index2.php";
});
}
},
});
});

The example given in the SWAL2 website isn't very suited for your use as it creates an input form itself while you already have an HTML form that gets input from the user. A basic easy-to-implement solution would be to use the showLoading and hideLoading methods with what you already have. From the documentation :
Swal.showLoading(): Shows loader (spinner), this is useful with AJAX
requests.
By default the loader be shown instead of the "Confirm" button, but if
you want another button to be replaced with a loader, just pass it
like this: Swal.showLoading(Swal.getDenyButton())
Swal.hideLoading(): Hides loader and shows back the button which was
hidden by .showLoading()
Just after the event.preventDefault(); line, you can add a loading dialog that will just display a loading message:
Swal.fire({
title: "Loading...",
html: "Please wait a moment"
})
Swal.showLoading()
Then, at the beginning of the success callback of your ajax request, just before entering the if condition, simply add a Swal.hideLoading() to stop displaying the loading animation.
Why does hideLoading and showLoading work on an already displayed popup?
The reason is because SweetAlert2 kind of (it seems to work like that but I didn't confirm it) works using static methods which takes their execution to a "global" scope, meaning that when you call one of SweetAlert2 methods, it runs and stores data in the same place: the Swal class which is shared everywhere. (If you want to know more about static class, there are several articles you can read on the internet)
Disclaimer! Now, I didn't take a look at how Swal stores data, or how it handles everything, but for the sake of argument, let's say it stores inside static properties like currentTitle, isLoading (which dictates whether or not a loading animation should be added to the popup), and more. That is effectively one reason why there's only one popup displayed at a given time (although by definition, there should be only one displayed).
So, when you call Swal.fire(), it essentially updates the Swal class properties (setting the currentTitle to Hello for example). When you call it again, it updates it again and effectively overwrites the values set by the previous call of fire(), replacing them with new ones.
But, unlike fire() which completely overwrites every property for the currently displayed popup, hideLoading and showLoading methods only overwrite ONE property: isLoading (reminder: this is how we've postulated SweetAlert works, the implementation could be made otherwise but for simplicity's sake, we assume it's like that) and let the others as is. That is why, when you use fire(), a new popup is displayed, but when using hideLoading and showLoading, the current popup displayed is changed.

Related

JQuery & Ajax disable click

I have a table with data and a function to help me get values from rows:
function getRow () {
$('#mytable').find('tr').click( function(){
let fname = $(this).find('td:eq(4)').text();
let start = $(this).find('td:eq(5)').text();
let end = $(this).find('td:eq(6)').text();
.......ajax method () etc
................
}
So far, it has been working perfectly and fetching me the correct data. I had another function elsewhere in the page, where clicking on some links would fetch some data from the server and reload the page to display the new data. Everything was working like clockwork.
Now, I decided that when re-displaying fresh data, instead of reloading the page, it's better to refresh the #mytable div. Indeed, it worked, but alas it spoiled the first function. So basically the function below has introduced a bug elsewhere in the page, and I'm not sure why or how to fix it. It's as if the div refresh has completely disabled the event handler. Any ideas?
$(document).ready(function() {
$(".key").click(function(event) {
event.preventDefault();
var word = event.target.innerHTML;
$.ajax({
url: '.../',
data: {
action : "key",
keyword: word
},
type: 'get',
success: function(data){
$('#mytable').load("/.../../..." + ' #ytable');
},
error: function(e){
console.log(e);}
});
});
});

Pass JQuery context $(this) to another function and retrieve context data

I'm using Metronic bootstrap admin them, which comes with sweetalert - which is an alert library
What I'm trying to do is to do a confirm alert on attempting to delete a record from the table , each row has a delete button
now Metronic has gone and did a little bit of extension on it, so you can now use html 5 "data" attributed to declaratively to declare title, button types etc.
in my MVC app, the following Razor code iterates and adds the button, note that I'm adding the data-id attribute to it - the idea being that I can extract it when the button is clicked to get the id to delete it
e.g.
<button data-id="#u.Id" type="button" class="btn btn-xs btn-circle red btn-delete mt-sweetalert" data-title="Are you sure?" data-type="warning" data-allow-outside-click="true" data-show-confirm-button="true" data-show-cancel-button="true" data-cancel-button-class="btn-danger" data-cancel-button-text="Cancel" data-confirm-button-text="Proceed" data-confirm-button-class="btn-info">
<i class="fa fa-times"></i>
</button>
the following is the Metronic JS extension file - I have added a few lines of code to it, so it calls a custom function on clicking the confirm option.
the idea being that I can leave the additions by Metronic Theme intact and call a custom function on the page where I need it
note that I'm also passing in the $(this) context to my custom function
var SweetAlert = function () {
return {
//main function to initiate the module
init: function () {
$('.mt-sweetalert').each(function(){
var sa_title = $(this).data('title');
var sa_message = $(this).data('message');
var sa_type = $(this).data('type');
var sa_allowOutsideClick = $(this).data('allow-outside-click');
var sa_showConfirmButton = $(this).data('show-confirm-button');
var sa_showCancelButton = $(this).data('show-cancel-button');
var sa_closeOnConfirm = $(this).data('close-on-confirm');
var sa_closeOnCancel = $(this).data('close-on-cancel');
var sa_confirmButtonText = $(this).data('confirm-button-text');
var sa_cancelButtonText = $(this).data('cancel-button-text');
var sa_popupTitleSuccess = $(this).data('popup-title-success');
var sa_popupMessageSuccess = $(this).data('popup-message-success');
var sa_popupTitleCancel = $(this).data('popup-title-cancel');
var sa_popupMessageCancel = $(this).data('popup-message-cancel');
var sa_confirmButtonClass = $(this).data('confirm-button-class');
var sa_cancelButtonClass = $(this).data('cancel-button-class');
$(this).click(function(){
//console.log(sa_btnClass);
swal({
title: sa_title,
text: sa_message,
type: sa_type,
allowOutsideClick: sa_allowOutsideClick,
showConfirmButton: sa_showConfirmButton,
showCancelButton: sa_showCancelButton,
confirmButtonClass: sa_confirmButtonClass,
cancelButtonClass: sa_cancelButtonClass,
closeOnConfirm: sa_closeOnConfirm,
closeOnCancel: sa_closeOnCancel,
confirmButtonText: sa_confirmButtonText,
cancelButtonText: sa_cancelButtonText,
},
function(isConfirm){
//action function on click
if (isConfirm){
swal(sa_popupTitleSuccess, sa_popupMessageSuccess, "success");
//custom function call added by me
//------------------------------------------
if(typeof onConfirmClick === "function")
onConfirmClick.call(this);
//------------------------------------------
} else {
swal(sa_popupTitleCancel, sa_popupMessageCancel, "error");
}
});
});
});
}
}
}();
jQuery(document).ready(function() {
SweetAlert.init();
});
The function in my page:
<script type="text/javascript">
function onConfirmClick() {
alert($(this).data('id'));
//make Ajax call here
}
</script>
Now the problem is, I'm getting the ($this) but not able to get the id or any attribute from the button for that matter
if I print $(this) in console
and just "this"
The question here is:
how can I get the data-id attribute in my function ? if I try $(this).data('id') - I get undefined
Is this the correct approach design wise ? I want to be able to call a custom function on confirm but not disrupt Metronic extensions on sweetalert ?
To pass the id from the button's data-id to the onConfirmClick() function...
I would try to make it transit via a variable.
So on click of a .mt-sweetalert, a SweetAlert is triggered.
Nothing stops you to have another click handler to store the id in a variable accessible by the function.
var sweetAlertId;
$(".mt-sweetalert").on("click", function(){
sweetAlertId = $(this).data("id");
});
Then in the function:
function onConfirmClick() {
alert(sweetAlertId);
//make Ajax request using sweetAlertId here!
}
In short, the id is not forced to transit via SweetAlert!
;)

How to remove jQuery element added by .append()?

General info
I'm working on a intranet based administration system for a distribution centre.
Situation
Basicly I have a contenteditable table with all user data. Except for the passwords of course. You could compare it with a webbased Excel sheet. Using a jQuery UI dialog, I'm popping op a form that allows the admin (company manager) of the system to change the employees passwords if clicked on a button.To make sure the password change will be applied to the correct user, I'm passing along the used id to my function that pops up the dialog. Using .append() I'm adding this id to the form. Up to this point everything works perfectly fine.
Problem
If the password change is cancelled, the id must be removed from the form again. Otherwise you end up appending more and more ids to the form on each user clicked. The same goes for when the password change is succeeded. I've tried doing this with jQuery .remove(), but it doesn't seem to work, even though I can't find any issue with the code.
Code
function changePass(id){
var addID = $("<input>")
.attr("type", "text")
.attr("id", "userid")
.attr("name", "userid").val(id);
$('#passChangeForm').append($(addID));
$("#changePass").dialog({
modal: true,
resizable: false,
title: "Change password",
buttons: [
{
text: "Cancel",
click: function() {
$("#passChangeForm").remove("#userid");
$(this).dialog("close");
}
},
{
text: "Ok",
click: function() {
$("#passChangeForm").submit();
}
}
]
});
}
$("#passChangeForm").on("submit", function(e){
e.preventDefault();
var password = document.getElementById("chPass1").value;
var password2 = document.getElementById("chPass2").value;
var userid = document.getElementById("userid").value;
$.ajax({
url: "system/changepass.php",
type: "POST",
data:'pass1='+password+'&pass2='+password2+'&id='+userid,
success: function(text){
alert(text);
$("#passChangeForm").remove("#userid");
$("#changePass").dialog("close");
}
});
});
Simply use this :
$( "#userid" ).remove();
Change the line
$("#passChangeForm").remove("#userid");
to
$("#userid").remove();

jQuery always prevents the action of the first click

I have several "Purchase Now" buttons of different articles. When the button has the class "sold-out" it shouldn't do anything otherwise it should open a jQuery Magnific Popup. So far that works. My problem is, that when I click for the first time I visit the homepage the purchaseable "Purchase Now" button, it isn't doing anything. When I click for the second time on it, it opens the jQuery window. But why it doesn't work for the first time already ??
My HTML:
Purchase Now
My JQuery:
$('.open-popup-link').magnificPopup({
type:'inline',
midClick: true,
mainClass: 'mfp-fade'
});
$('.ajax-popup').click(function(e){
e.preventDefault();
if($(this).hasClass("sold-out")) {
return false;
}
var region = $(this).data('region');
var quantity = $(this).data('quantity');
if(typeof quantity == 'undefined') quantity = $(this).parent().find('select').val();
var packageid = $(this).data('packageid');
$(this).magnificPopup({
type: 'ajax',
ajax: {
settings: {
data : {
region : region,
quantity : quantity,
packageid : packageid,
}
}
},
closeOnContentClick: false,
closeOnBgClick: false
});
});
It might be because you are declaring the popup definition within the click function. Can you try declaring the function outside the click function?
It doesn't open on first click cause this is the time you BIND it to the element.
Hovewer, we see in documentation that we can instantly open MagnificPopup.
// Open popup immediately. If popup is already opened - it'll just overwite the content (but old options will be kept).
// - first parameter: options object
// - second parameter (optional): index of item to open
$.magnificPopup.open({
items: {
src: 'someimage.jpg'
},
type: 'image'
// You may add options here, they're exactly the same as for $.fn.magnificPopup call
// Note that some settings that rely on click event (like disableOn or midClick) will not work here
}, 0);
http://dimsemenov.com/plugins/magnific-popup/documentation.html#options

jquery Select2 prevent selecting in ajax response

I want to prevent from adding a category to the Select2 element if it fails creating the row first in my db. The action is not prevented when i call ev.preventDefault(); Nothing happens.. what is wrong?
$('#sel2').select2({
placeholder: 'Enter categories',
minimumInputLength: 3,
multiple: true,
ajax: {
url: 'async/get_categories.php',
dataType: 'json',
quietMillis: 250,
data: function (term, page) {
return {
q: term,
};
},
results: function (data, page) {
return {
results: data.items
};
},
cache: true
},
formatResult: format,
formatSelection: format
}).on('select2-selecting', function(e) {
console.log(e);
if (e.val == 4) {
// if category id equals 4
// do not add this category to select 2
// e.preventDefault();
// the above works just fine and its just for testing
}
// Is something wrong here?
var ev = e;
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: ev.val
},
success: function(response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
ev.preventDefault();
// the above is not working
}
},
error: function() {
alert('Failed to assign category!');
}
});
});
The AJAX request is made asynchronusly, so by the time it has finished the element has already been added. Even though you are calling ev.preventDefault(), it is too late for it to make a difference. So this leaves you with two options:
Make the request synchronusly, which will allow preventDefault to make the difference.
Make the request asynchronusly, and manually remove the element if it fails.
Both options have their pros and cons, and it's up to you to decide which option you go with.
Making the request synchronusly
Pros
The value will never be added if the request fails.
Works well in cases where the element cannot be added quite often.
Cons
Blocks the UI - So the user is potentially left with an unresponsive page while the request is made.
Making the request asynchronusly
Pros
Does not block the UI.
Works well in cases where elements typically can be added.
Cons
The value will always show up for the user, even if it fails later.
You must manually unset the new option.
What's important to consider here is the user experience of both options. When making synchronus requests, it's not uncommon for the browser to stop relaying events - which gives the illusion that the UI has locked up and the page has gone unresponsive. This has the benefit of ensuring that the value never shows up if it isn't allowed. But if users typically can add the elements, it also has the downside of complicating the most common use case.
If users can usually add elements, then it is a better experience to add the element while the request is being made, and then notifying the user later (while removing the element) if there was an issue. This is very common is web applications, and you can see it being used in many places, such as the Twitter and Facebook like buttons (where requests usually work), as well as places on Stack Overflow.
There is a way to get around this with version4 of the select2 library.
on select2:selecting we cancel the preTrigger event. Which will stop the select2:select event. We do our ajax call. On success we then get out Select2 instance then call the trigger of the Observer that way it by passes overwritten trigger method on your select2 instance.
The call method needs your select2 instance as the context so that the existing listeners are available to call.
var sel = $('#sel');
sel.select2(config);
sel.on('select2:selecting', onSelecting);
function onSelecting(event)
{
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: event.params.args.data.id
},
success: function(event, response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
// get select2 instance
var Select2 = $users.data('select2');
// remove prevented flag
delete event.params.args.prevented;
// Call trigger on the observer with select2 instance as context
Select2.constructor.__super__.trigger.call(Select2, 'select', event.params.args);
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
}
}.bind(null, event),
error: function() {
alert('Failed to assign category!');
}
});
event.preventDefault();
return false;
}
here how I did it for yii2 Select2 integrated into Gridview:
'pluginEvents' => [
'select2:selecting' => "
function(event)
{
var select2 = $('#types-" . $model->id . "');
select2.select2('close');
$.post('update',{id: " . $model->id . ", type_id: event.params.args.data.id})
.done (function(response)
{
select2.val(event.params.args.data.id);
select2.trigger('change');
})
.fail(function(response)
{
krajeeDialog.alert('Error on update:'+response.responseText);
});
event.preventDefault();
return false;
}",
],
it allows to asynchoronous update data in the grid using select2 and ajax and return it to previous value if there was an error on updating.

Categories

Resources