How to detect that unobtrusive validation has been successful? - asp.net-mvc

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.

Related

Add a custom textfield to button_to in addition to data

I have a button_to called 'DELETE' within which I have added
data: { confirm: "Are you sure you want to delete?" }
I also wish to add a custom textfield asking the user for appropriate reason before deletion and then store it consequently. Can I add a textfield inside data apart from the default confirm or disabled option?
I have done it with window.open as of now but it's just a workaround.
You can't add additional fields to the confirmation box. Because it gets only single parameter - message. See here.
I'd recommend to built a custom confirmation dialog for this task.
Firstly, button_to is not the same as data: {confirm....} (although thinking about it, you're probably using data: {...} on the button_to)
If you were using button_to, you could add extra parameters to your form by using the params option:
<%= button_to "Text", your_path, params: {name: "John"} %>
As described in the docs, the params are passed as hidden fields, and thus should be static data (not editable by the user):
Hash of parameters to be rendered as hidden fields within the form.
Since you wish to use data: {confirm ...}, you have to be clear on how it works:
confirm: 'question?' - This will allow the unobtrusive JavaScript driver to prompt with the question specified (in this case, the resulting text would be question?. If the user accepts, the link is processed normally, otherwise no action is taken.
As stated, this loads a "confirm" JS dialogue, which basically only has ok/cancel to determine whether the user wishes to proceed.
You cannot send extra parameters through the standard confirmation dialogue.
--
What you can do is create a custom confirmation action for Rails.
This involves overriding the $.rails.showConfirmationDialog(link); method, so instead of invoking a lowly confirm dialogue, it can show whatever you need.
Here's a gist:
#app/assets/javascripts/application.js
$.rails.allowAction = function(link) {
if (link.data('confirm')) {
$.rails.showConfirmationDialog(link);
return false;
} else {
return true;
}
};
$.rails.confirmed = function(link) {
link.data('confirm', null);
return link.trigger('click');
};
$.rails.showConfirmationDialog = function(link) {
var message, title;
message = link.data('confirm');
title = link.data('title') || 'Warning';
return // ->> your custom action <<-- //
};
We use the following:
#app/assets/javascripts/application.js
var myCustomConfirmBox;
$.rails.allowAction = function(element) {
var answer, message;
message = element.data("confirm");
answer = false;
if (!message) {
return true;
}
if ($.rails.fire(element, "confirm")) {
myCustomConfirmBox(element, message, function() {
var callback, oldAllowAction;
callback = $.rails.fire(element, "confirm:complete", [answer]);
if (callback) {
oldAllowAction = $.rails.allowAction;
$.rails.allowAction = function() {
return true;
};
element.trigger("click");
$.rails.allowAction = oldAllowAction;
}
});
}
return false;
};
myCustomConfirmBox = function(link, message, callback) {
var flash, icon, wrap;
if (!($("flash#confirm").length > 0)) {
icon = document.createElement("i");
icon.className = "fa fa-question-circle";
flash = document.createElement("flash");
flash.setAttribute("id", "confirm");
flash.appendChild(icon);
flash.className = "animated fadeInDown";
flash.innerHTML += message;
wrap = document.getElementById("wrap");
wrap.insertBefore(flash, wrap.childNodes[0]);
return $(document).on("click", "flash#confirm", function() {
return callback(link);
});
}
};
--
If you wanted to pass an extra parameter through this, you'd have to use it in the JS. I've never done it before, but I know you can append it to the query you're sending to your server.
As such, if you update your code to show your routes and controller code, I should be able to come up with an idea on how to pass a param for you.

SignalR and Kendo Ui Scheduler

I'm working in an implementation using SignalR and the Kendo Scheduler. When a new task is created (for exemple), the SchedulerDataSource transport send the connection hub id to the server as an additional parameter:
transport: {
read: { url: global.web_path + 'Home/Tasks' },
update: { url: global.web_path + 'Home/UpdateTask', type: 'PUT', contentType: 'application/json' },
create: { url: global.web_path + 'Home/CreateTask', type: 'POST', contentType: 'application/json' },
destroy: { url: global.web_path + 'Home/DeleteTask', type: 'DELETE', contentType: 'application/json' },
parameterMap: function (options, operation) {
if (operation == "destroy" && options.models) {
return JSON.stringify({ taskId: options.models[0].Id, callerId: $.connection.hub.id });
}
if (operation !== "read" && options.models) {
return JSON.stringify({ tasks: options.models, callerId: $.connection.hub.id });
}
}
},
The server do whatever it has to do, and send a notification to every other user, except de caller:
[HttpPost]
public JsonResult CreateTask(List<ScheduledEvent> tasks, string callerId)
{
...create task and other stuff
//broadcast the newly created object to everyone except caller
var hubContext = GlobalHost.ConnectionManager.GetHubContext<Notebooks.Hubs.SchedulerHub>();
hubContext.Clients.AllExcept(callerId).UpdateSchedule(task);
//return the object to caller
return Json(task);
}
Once the other clients receive a new task from the hub, it is added to the SchedulerDataSource:
hub.client.updateSchedule = function (scheduledEvent) {
schedulerDataSource.add(scheduledEvent);
}
Everything seems to work fine, and it really took me some time to realize this behavior: if a client have the scheduler window open, this window is closed once the schedulerDataSource is updated. This is expected or am I doing something wrong?
Edit: I just realized how old this question is, so you have probably moved on to other things by now, or the pushCreate method may not have existed back then.
I think this may be how it works, but it seems like it should be able to add those events behind the scenes without having to close the edit window. Have you tried the pushCreate method? If that doesn't work, since the add automatically closes the edit dialog, maybe when the events come in, if the dialog is open, you could store the new events, then add them when the user closes the edit dialog.
My answer is now even older ;) but I faced this very same issue today.
First, I'm quite sure this is indeed the expected behavior. You can see in the kendo sources the call of the close editor window method in the transport update and create methods of the scheduler.
Below is what I've done to bypass the issue .
The idea is as simple as to prevent the edit window to close when an appointment modification comes from another hub client.
Server-side : modify the hub methods (example with update method)
public MyAppointmentViewModel Update(MyAppointmentViewModel appointment)
{
if (!appointmentService.Update(appointment))
throw new InvalidOperationException("Something went wrong");
else
{
Clients.Others.PrepareBeforeAddOrUpdateSignal(appointment.Id);
Clients.Others.Update(appointment);
return appointment;
}
}
Here you see we inform every other clients (through PrepareBeforeAddOrUpdate) we're about to update an appintment.
Client-side now (in index.cshtml for instance)
schedulerHub.client.prepareBeforeAddOrUpdateSignal = function(id){ lastModifiedRdvId = id; };
schedulerHub.client.create = function(appointment){ lastModifiedRdvId = 0; }; /* reset the variable for next time */
schedulerHub.client.update = function(appointment){ lastModifiedRdvId = 0; }; /* reset the variable for next time */
function SchedulerEditor()
{
return $(".k-scheduler-edit-form").data("kendoWindow");
}
var eventBeforeChanges = null;
var lastModifiedRdvId = 0;
function onEditorClose(e) {
if (eventBeforeChanges != null) {
if (lastModifiedRdvId > 0 && eventBeforeChanges.Id != lastModifiedRdvId)
e.preventDefault();
else {
var editWin = SchedulerEditor(); /* unbind this callback and use default behavior again */
editWin.unbind('close', onEditorClose);
}
}}
function onEditRdv(e) {
var editWin = SchedulerEditor();
if (editWin != null) /*Bind the close event */
editWin.unbind('close', onEditorClose).bind('close', onEditorClose);
eventBeforeChanges = e.event;
/* continue onEditRdv */
}
you see here the close event is prevented when the appointment id is not the appointment id beeing updated by the current client.
And fortunately, preventing the close event prevents the annoying behavior of having a sort of ghost appointment after one has been changed by another hub client.
I'm sorry if I'm not clear or if the code isn't clear enough. I can provide more information if necessary.
Bye

Can I suppress MVC Client validation from running while programmatically changing input values?

I have a form where the input boxes are prefilled with default values. A jQuery event handler clears the value on 'focus' and restores the default value on 'blur', if the field is empty.
$('form input').on('focus blur', function (event) {
var eventTarget = $(this);
if (eventTarget.val() === eventTarget.data('default')
|| eventTarget.val() === '') {
if (event.type === 'focus') {
eventTarget.val('').removeClass('empty');
} else {
eventTarget.val(eventTarget.data('default')).addClass('empty');
}
}
}).trigger('blur');
Before submitting (via AJAX), I empty all fields that contain the default value and trigger client side validation (for required fields):
$('form').on('submit', function (e) {
$('form input').each(function () {
if ($(this).val() === $(this).data('default')) {
$(this).val(''); // remove default value, so 'required' validation triggers on empty fields
}
});
if ($('form').valid()) {
$.ajax(...);
} else {
$('form input').trigger('blur'); // trigger 'blur' to restore default values
// Problem: This clears the validation messages
}
});
So far, so good, however after that, my form fields are of course empty. If I trigger the 'blur' event again, I can restore the default values, but that makes the validation messages disappear, probably because client side validation runs again and sees that the fields are no longer empty.
When I manually click in and out of the form field, though, the default value is restored and the validation message stays. Both go through the same 'blur' handler. I cannot figure out where the difference is.
Edit:
I have since figured out that I can disable validation on 'blur' completely:
$('form').validate().settings.onfocusout = false;
This works around my issue for now, but I would still like to know why blur/focusout reacts differently when triggered from a script or from user interaction.

How to re-trigger a form submit event in jQuery UI dialog callback

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.

JQueryUI Tabs Selection and Validation

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.

Resources