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.
Related
I'm doing a simple AJAX call to append an album's tracks in an unordered list. It will append the tracks on the second click with this code:
window.app.views.AlbumView = Backbone.View.extend({...
events: {
'click .queue-add' : 'selectAlbum',
'click .show-tracks' : 'showTracks',
'click .hide-tracks' : 'hideTracks',
},
showTracks: function(){
_this = this
this.model.getTracks().forEach(function(track){
_this.$el.find('.tracks').append("<li>"+track.attributes.title+"</li>");
});
},
Clearly the tracks hadn't been fetched in time for the first click so I added a callback function to the showTracks method like so:
showTracks: function(){
_this = this
this.model.getTracks({
success: function(tracks){
console.log(tracks);
tracks.forEach(function(track){
_this.$el.find('.tracks').append("<li>"+track.attributes.title+"</li>");
});
}
});
},
Yet it won't enter the block and the console.log(tracks); puts nothing to the console.
Any tips would be really awesome here, thanks!!
app.models.Album = Backbone.Model.extend({
....
getTracks: function() {
this.tracks.fetch();
return this.tracks
},
....
});
I couldn't find where did you invoke that callback. you may need modify "getTracks" method like this:
getTracks: function(callback) {
this.tracks.fetch();
callback(this.tracks); //you need to invoke the callback before return
return this.tracks;
}
This is called "callback pattern", google it will find more.
and the backbone model's fetch method accept option argument, It is a object with two keys -- success and error -- both are function. If you provide this argument, backbone will call them automatically.
hope this help.
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!
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 have this code that triggers when a form is submitted:
$("form").submit(function (e) {
var geocoder = new google.maps.Geocoder();
var address = document.getElementById("Address").value;
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("#LatitudeLongitude").val(results[0].geometry.location);
$("form").submit();
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
$('form').unbind('submit');
return false;
});
What it does: it calls google geocoding service to translate an address into latitude/longitude which is set into a hidden field of the form. If there is a result, then the form is submitted.
The problem is that if validation fails (for instance a required field has not been set) then the call to geocoding is still made. Moreover, if I click a second time on the submit button, even if the required field has not been set, the form is posted.
How can I call the geocoding service only if the unobtrusive validation has been successful?
Rather than attaching to the submit() event, you need to capture an earlier event, and exercise control over how to proceed.
First, let's assume your original button has an id of submit and you create a new submit button with an id of startSubmit. Then, hide the original submit button by setting the HTML attribute display="false". Next, bind to the click event of your new button, and add your code, as modified:
$("#startSubmit").live("click", function() {
// check if the form is valid
if ($("form").validate().form()) {
// valid, proceed with geocoding
var geocoder = new google.maps.Geocoder();
var address = $("#Address").val();
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("#LatitudeLongitude").val(results[0].geometry.location);
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
// proceed to submit form
$("#submit").click();
}
}
return false;
});
This will invoke validation, so that geocoding will only occur if the form is valid, then, after geocoding has returned a response, it will submit the form by incoking the click event on the submit button.
I implemented something similar (with thanks to cousellorben), in my case I wanted to disable and change the text of the submit button but only on success. Here's my example:
$("#btnSubmit").live("click", function() {
if ($("form").validate().form()) {
$(this).val("Please Wait...").attr("disabled", "disabled");
$("form").submit();
return true;
} else {
return false;
}
});
The only difference is that my example uses a single button.
This answer may help you, it gives a quick example of how a function is called when the form is invalid, based on the use of JQuery Validate, which is what MVC uses.
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.