I am having a hard time with the new Alpha release of JQM not showing nested popups. For example, I am displaying a popup form that the user is supposed to fill out, if the server side validation fails, I want to display an error popup. I am finding that the error popup is not being shown. I suspect that it is being shown below the original popup.
function bindSongWriterInvite() {
// Bind the click event of the invite button
$("#invite-songwriter").click(function () {
$("#songwriter-invite-popup").popup("open");
});
// Bind the invitation click event of the invite modal
$("#songwriter-invite-invite").click(function () {
if ($('#songwriter-invite').valid()) {
$('#songwriter-invite-popup').popup('close');
$.ajax({
type: 'POST',
async: true,
url: '/songwriter/jsoncreate',
data: $('#songwriter-invite').serialize() + "&songName=" + $("#Song_Title").val(),
success: function (response) {
if (response.state != "success") {
alert("Should be showing error dialog");
mobileBindErrorDialog(response.message);
}
else {
mobileBindErrorDialog("Success!");
}
},
failure: function () {
mobileBindErrorDialog("Failure!");
},
dataType: 'json'
});
}
});
// Bind the cancel click event of the invite modal
$("#songwriter-invite-cancel").click(function () {
$("#songwriter-invite-popup").popup("close");
});
}
function mobileBindErrorDialog(errorMessage) {
// Close all open popups. This is a work around as the JQM Alpha does not
// open new popups in front of all other popups.
var error = $("<div></div>").append("<p>" + errorMessage + "</p>")
.popup();
$(error).popup("open");
}
So, you can see that I attempt to show the error dialog regardless of whether the ajax post succeeds or fails. It just never shows up.
Anyone have any thoughts?
Mike
I found a solution!
I set a delay on the bindErrorPopup function to wait until the popup close animation completes.
function mobileBindErrorDialog(errorMessage, delay) {
var error = $("<div></div>").attr({
'data-rel': "popup",
'data-theme': "a"
});
$(error).append("<p>" + errorMessage + "</p>")
.popup({
overlayTheme: "a",
positionTo: "window",
theme: "a"
});
setTimeout(function () {
$(error).popup("open");
}, delay);
}
Works awesome!
Related
I have a function being called on a page on a local apache instance (/test) which calls a subpage (/test/info) with jQuery.ajax and correctly makes an AJAX call and dynamically loads the content from the response on my desktop in FF, Safari, Chrome, but in the iOS emulator, no call is made and the page is refreshed.
window.getInfo = function ( event ) {
console.log('1) prior to $.ajax');
$.ajax({
url: 'http://localhost/test/info',
dataType: 'html',
beforeSend: function(xhr) {
console.log('2) beforeSend');
},
success: function(data, textStatus) {
console.log('3) success');
if ( textStatus == 'success' ) {
// doing stuff with data
}
}
}).always( function() { console.log('4) always'); });
};
From the desktop browsers all of the logs are printed and my apache server reports a request at /test, but on Safari in the iOS emulator, only the '1) prior to $.ajax' and '2) beforeSend' logs are printed and the next request made to apache is for /test.
Does anyone have any idea what is happening here, and how to make iOS behave itself?
UPDATE: When I add the async: false attribute to the ajax call, all the logs are printed, and the request is made, so that basically fixed the issue; however the page still reloads which I believe is a different issue related to event propagation on iOS.
All that is needed to make this work is a return false; from the handler function. See event.preventDefault() vs. return false for a more complete explanation, but basically "return false from within a jQuery event handler is effectively the same as calling both e.preventDefault and e.stopPropagation on the passed jQuery.Event object."
So a fully functional version of the above code is:
getInfo = function() {
$.ajax({
url: '/test/info',
dataType: 'html',
success: function(data, textStatus) {
if ( textStatus == 'success' ) {
// doing stuff with data
}
}
});
return false;
};
// The order of vv that string may be important
$(document).on('click touchend', '.getInfo', getInfo );
I'm using jQuery UI autocomplete with data from a remote datasource. My use case is really similar to the example here:
http://jqueryui.com/demos/autocomplete/#remote
The only difference is that I set my delay to 0. In between the keystrokes, the menu disappears for about 1/10th of a second ~100milli seconds prior to the updated autocomplete list being displayed.
Is there anyway I can prevent the menu from temporarily disappearing between keystrokes? A good use case is google's search, where between keystrokes, the suggestion box does not temporarily disappear.
IMO, it is not a good practice to set a delay of zero when using a remote datasource. It will send more requests than needed and surcharge the server with no benefit.
Anyway, I think you can achieve what you want by defining the source option as a callback yourself.
First a bit of explanaton. I suppose you are using the remote feature passing an url as the source for the plugin. The plugin actually wraps this into a callback implemented this way:
// in case the option "source" is a string
url = this.options.source;
this.source = function(request, response) {
if (self.xhr) {
self.xhr.abort();
}
self.xhr = $.ajax({
url: url,
data: request,
dataType: "json",
autocompleteRequest: ++requestIndex,
success: function(data, status) {
if (this.autocompleteRequest === requestIndex) {
response(data);
}
},
error: function() {
if (this.autocompleteRequest === requestIndex) {
response([]);
}
}
});
};
As you can see, if there is already an ajax request going on, it abords it. This happenning in your case as a request, as fast as your server can be, takes some time and your delay is zero.
if (self.xhr) {
self.xhr.abort();
}
This will actually execute the error callback of the aborted request that will execute itself the response callback with an empty dataset. If you look at the response callback, it closes the menu if data is empty:
_response: function(content) {
if (!this.options.disabled && content && content.length) {
...
} else {
this.close();
}
You can actually define your own source callback to make your ajax request yourself and change the default behavior by not aborting any pending request. Something like:
$('#autocomplete').autocomplete({
source: function(request, response) {
$.ajax({
url: url,
data: request,
dataType: "json",
success: function(data, status) {
// display menu with received dataset
response(data);
},
error: function() {
// close the menu on error by executing the response
// callback with an empty dataset
response([]);
}
});
}
});
I'm trying to use a jQuery UI Dialog for a sort of "soft" validation--if the form looks suspicious, warn the user, but allow them to continue the submission anyway:
// multiple submit buttons...
var whichButton;
$("#myForm").find("[type=submit]").click(function()
{
whichButton = this;
}
var userOkWithIt = false;
$("#myForm").submit(function()
{
if (dataLooksFishy() && !userOkWithIt)
{
$("Are you sure you want to do this?").dialog(
{
buttons:
{
"Yes": function()
{
$(this).dialog("close");
// override check and resubmit form
userOkWithIt = true;
// save submit action on form
$("#myForm").append("<input type='hidden' name='" +
$(whichSubmit).attr("name") + "' value='" +
$(whichSubmit).val() + "'>");
$("#myForm").submit(); /****** Problem *********/
},
"No": function() { $(this).dialog("close"); }
}
});
return false; // always prevent form submission here
} // end data looks fishy
return true; // allow form submission
});
I've checked this out with a bunch of debugging alert statements. The control flow is exactly what I expect. If I first fail dataLooksFishy(), I am presented with the dialog and the method returns false asynchronously.
Clicking "yes" does re-trigger this method, and this time, the method returns true, however, the form does not actually submit...
I'm sure I'm missing a better methodology here, but the main target is to be able to simulate the behavior of the synchronous confirm() with the asynchronous dialog().
If I understand your problem correctly - here's the solution.
(I've separated actions into separate functions (easier to manage)):
submitting the form (normally) would check if there are errors -
dataLooksFishy()
if there are errors - a dialog should pop-up
if user clicks "yes" - form will be submitted with "force=true"
var
form = $("#myForm"),
formSubmit = function(force){
if (dataLooksFishy() && force !== true) return showWarning(); // show warning and prevent submit
else return true; // allow submit
},
showWarning = function(){
$("Are you sure you want to do this?").dialog({ buttons: {
"Yes": function(){ $(this).dialog("close"); formSubmit(true); },
"No": function() { $(this).dialog("close"); }
}});
return false;
},
dataLooksFishy = function(){
return true; // true when there are validation errors
};
// plain JS form submitting (also works if you hit enter in a text field in a form)
form[0].onsubmit = formSubmit;
I couldn't test it with your form as you have not posted it here.
If you have problems with this solution, please post more of your code here and I'll try to help.
I am trying to validate tab content(using ajax validation) before switching to the next tab. So when the user clicks on the tab, the current tab content is sent to the server for validation. And when result from the server is received I switch to the next tab. Here is some code:
$('#tabs').tabs({
select: function(event, ui) {
...
validate(currentIndex, nextIndex);
return false;
}
});
function validate(currentIndex, nextIndex){
$.ajax({
...
complete: function(){
$("#tabs").tabs('select', nextIndex);
}
...
}
}
You probably can see the problem already, it's infinite loop, because validation causes tab's select handler that causes validation and so on. How would I solve this without global variables?
Thanks.
Please take a look on this - official Jquery tabs documentation
Your validate function should not do anything except returning true or false.
$('#tabs').tabs(
{
select: function(event, ui)
{
...
return validate(currentIndex, nextIndex);
}
});
function validate(currentIndex, nextIndex)
{
$.ajax(
{
...
success: function(data)
{
// example response: {"error": 0}
if(!data.error) return true;
else return false;
}
...
}
}
When your tabs('select'... function returns false, the tab won't switch, if true the tab will switch - so you don't have to do it in your validate function.
I'm not sure if this would work (haven't tried your code) but couldn't you use the .data(key,value) to add some sort of a "already validated" flag to check for? So you add an if statement which checks for this before entering the validation function again.
I'm using jQuery in my ASP.NET MVC 2 page. It's a super simple post that gets a date in return. The process itself runs very quickly. I want to use a spinner image to show the user something is going on. All works fine in Firefox, testing locally. However, in IE 8, the spinner image doesn't display long enough; it just flickers. This might confuse the end user if they do not see an indication that the process is working/done. What would be the best way to fix this problem?
Thanks.
<img id="signedout<%: item.Id %>" src="/Content/Images/signed_out.png" alt="Signed Out" title="Signed Out" style="display:none" />
<div id="ajaxloader<%: item.Id %>" style="display:none;text-align:center"><img src="/Content/Images/ajax-loader.gif" alt="loading..." title="loading..." /></div>
<script type="text/javascript">
$(function () {
var id = "<%: item.Id %>";
$("#signout" + id).click(function () {
if (confirm("Are you sure you want to sign this visitor out?")) {
$(this).hide();
//$("#signout" + id).hide();
$("#ajaxloader" + id).show();
var url = '<%: Url.Action("signout") %>';
$.ajax({
type: "POST",
url: url,
data: "id=" + id,
success: function (result) {
$("#timeout" + id).append(result);
$("#ajaxloader" + id).hide();
$("#signedout" + id).show();
$("#row" + id).css("background", "#E8E8E8");
},
error: function () {
alert("There was an unexpected error. Please try again later.");
$("#ajaxloader" + id).hide();
$("#signout" + id).show();
}
});
}
});
});
</script>
It would probably be a better idea to give some other indication that the process has finished rather than misleadingly displaying the spinner when nothing is actually being processed. However you seem set on this.
This is based on amurra's answer and is designed to either hide the spinner once the process is loaded if and only if the timeout has already been returned. Therefore, if the AJAX request returns "too fast" it will wait for the timeout.
var timeoutComplete=false;
var requestComplete=false;
function HideLoadingSpinner() {
if(requestComplete) {
$("#ajaxloader" + id).hide();
} else {
timeoutComplete=true;
}
}
Meanwhile, inside the click handler...
setTimeout(HideLoadingSpinner, 2000);
$.ajax({type: "POST",
url: url,
data: "id=" + id,
success: function (result) {
$("#timeout" + id).append(result);
$("#signedout" + id).show();
$("#row" + id).css("background", "#E8E8E8");
if(timeoutComplete) {
$("#ajaxloader" + id).hide();
} else {
requestComplete=true;
}
},
error: function () {
alert("There was an unexpected error. Please try again later.");
$("#ajaxloader" + id).hide();
$("#signout" + id).show();
}
});
EDIT: I just removed the if(timeoutComplete) stuff from the error function, since there will be an alert before it anyway.
You could set a timeout in js and that would guarantee that the spinner wouldn't be hidden till after the timeout had expired:
function HideLoadingSpinner() {
$("#ajaxloader" + id).hide();
}
Then replace your ajax call with this one:
$.ajax({type: "POST",
url: url,
data: "id=" + id,
success: function (result) {
$("#timeout" + id).append(result);
setTimeout(HideLoadingSpinner, 2000);
$("#signedout" + id).show();
$("#row" + id).css("background", "#E8E8E8");
},
error: function () {
alert("There was an unexpected error. Please try again later.");
setTimeout(HideLoadingSpinner, 2000);
$("#signout" + id).show();
}
});
You could also move the other logic in the success callback into the HidingLoadingSpinner function if you get weird behavior due to the timer.
Can't you just show "Data loaded" message in bright background with fades off in few seconds after reload?
I've seen this before. Actually what you're seeing is IE taking a normal amount of time to run the request and FF taking longer than it should. You can set network.dns.ipv4OnlyDomains to localhost in about:config to dramatically speed up FF on localhost. Source.
In my own applications, I follow #Im0rtality's suggestion and have a notification that an ajax request is complete and show a spinner while it's happening. Many times I don't even see the spinner. I use a custom implementation of JBar for my notifications which is very professional looking.
As far as showing a spinner during AJAX calls, you include this in your master page to show a spinner during all JQuery AJAX calls:
$(function () {
$('.spinner')
.hide()
.ajaxStart(function () {
$(this).show();
})
.ajaxStop(function () {
$(this).hide();
});
});
Then you don't have to include showing and hiding the spinner w/ every call to $.ajax.