问题描述
我遇到了一个非常奇怪的情况.解释起来很复杂,但我会尽力的.
问题的详细说明:
在每个顶部导航点击(绿色甜甜圈/圆圈)或下一个按钮上,我必须提交表单(如果它存在且有效).如果无效,则 form.valid() 触发验证错误并返回 false 将停止任何进一步的传播.这个设置完美无缺,直到我注意到一个不是很持久的奇怪行为.具体来说,我的第三个选项卡上的表单数据量很大.当我点击下一个按钮时,它实际上应该通过相同的过程:检查现有表单,如果有效,然后提交.Submit 调用 POST 操作方法,当发布完成时,它会获取下一个选项卡的视图.它像这样工作 5/10 次,但在其他时候 GET 在 POST 之前执行,这会导致下一页加载不完整的数据.当我设置断点进行调试时,我看到在当前选项卡的 POST 之前执行的下一个选项卡的 GET.
用户界面解释:
我有一个 UI,顶部有 4 个导航 <a>
按钮 - 在中心总是有一个表单 - 在底部我有 Previous &下一个按钮.
使用 Ajax.BeginForm
对于顶部的每个导航链接 元素,我有一个 JavaScript 函数
var LoadTabs = function (e, arg) {//这是为了在单击顶部链接之一并且表单具有不完整的字段时验证表单...if (arg !== "prev" && arg !== "next") {如果 (!window.ValidateForm(false)) 返回 false;}var url = $(this).attr('data');//这包含指向 GET 操作方法的链接如果(网址类型!=未定义"){$.ajax(url, { context: { param: arg } }).done(function (data) {$('#partialViewContainer').html(data);});}}
$('.navLinks').on('click', LoadTabs);
我的下一个 &以前的按钮基本上触发点击事件,即 LoadTabs
功能.
$('button').on('click', function () {if (this.id === "btnMoveToNextTab") {如果 (!window.ValidateForm(true)) 返回 false;$.ajax({网址:网址,上下文:{参数:'下一个'},方法:获取",数据:数据,成功:功能(响应){if (typeof response == 'object') {如果(response.moveAhead){移动下一步();}} 别的 {$('#mainView').html(响应);}向上滚动(0);}});}if (this.id === "btnMoveToPreviousTab") {后退();}返回假;});MoveNext() 实现如下:函数移动下一步(){var listItem = $('#progressbarInd > .active').next('li');listItem.find('.navLink').trigger('click', ['next']);向上滚动(0);}
我的 ValidateForm 方法基本上只是检查表单是否存在并且有效然后提交,否则返回 false.如下:
function ValidateForm(submit) {var form = $('form');//如果页面上不存在表单 - 返回 true 并继续if (typeof form[0] === "undefined") 返回真;//现在检查任何验证错误如果(提交){如果 (!$(form).valid()) {返回假;} 别的 {$(form).submit();}}别的 {返回真;}返回真;}
我的推测是 form.submit 确实被触发了,但由于提交需要更长的时间才能完成,所以它会继续按钮 onclick
事件中的下一个代码块.
我首先认为这是一个服务器端问题,因为在 POST 中我正在保存大量数据和几个循环,并且任何处理繁重的代码块我都有那部分
请如果有人能帮我找到正确的方向,我将不胜感激.如果您需要更多信息,请告诉我,我很乐意提供!
- 您调用
ValidateForm()
函数(并假设其有效),您的$(form).submit();
代码行开始制作 ajax POST - 代码前进到最后的
return true;
行,而 ajax调用正在执行. - 因为
ValidateForm()
函数返回 true,$.ajax
GET 调用现在开始,但此时 ajax POST 中的ValidateForm()
函数可能没有完成执行导致您的 GET 方法返回无效数据
$(document).on('submit', 'form', function(e) {e.preventDefault();//取消默认提交var form = $(this);如果(!form.valid()){返回;//将显示验证错误}....//获取 GET 和 POST 方法等的相关 url$.post(postUrl, form.serialize(), function(data) {....//不清楚您的 [HttpPost] 方法是否返回任何内容}).完成(功能(){$.get(getUrl, someData, function(response) {....//用下一个表单更新 DOM?....//重新解析验证器以进行客户端验证}}).失败(函数(){....//如果 [HttpPost] 方法中的代码失败,您可能会运行的代码});});
您还应该考虑在 [HttpPost]
方法中返回适当的next"视图,这样您就不需要再次调用服务器来获取它.>
还值得阅读延迟对象文档和$.when()、$.then()
等
I'm stuck in a really bizarre situation here. It's complicated to explain but I'll try my best.
Detailed explanation of the issue:
UI Explained:
I have a UI with 4 navigation <a>
buttons on top - in the center there's a always a form - and at the bottom I have Previous & Next buttons.
Forms are constructed in MVC using Ajax.BeginForm
For each Nav link <a>
element on top, I have a JavaScript function
var LoadTabs = function (e, arg) {
// This is to validate a form if one of the top links is clicked and form has incomplete fields...
if (arg !== "prev" && arg !== "next") {
if (!window.ValidateForm(false)) return false;
}
var url = $(this).attr('data'); // this contains link to a GET action method
if (typeof url != "undefined") {
$.ajax(url, { context: { param: arg } }).done(function (data) {
$('#partialViewContainer').html(data);
});
}
}
This function above binds to each top link on page load.
$('.navLinks').on('click', LoadTabs);
My Next & Previous buttons basically trigger the click event i.e. LoadTabs
function.
$('button').on('click', function () {
if (this.id === "btnMoveToNextTab") {
if (!window.ValidateForm(true)) return false;
$.ajax({
url: url,
context: { param: 'next' },
method: "GET",
data: data,
success: function(response) {
if (typeof response == 'object') {
if (response.moveAhead) {
MoveNext();
}
} else {
$('#mainView').html(response);
}
ScrollUp(0);
}
});
}
if (this.id === "btnMoveToPreviousTab") {
MoveBack();
}
return false;
});
MoveNext() Implementation is as below:
function MoveNext() {
var listItem = $('#progressbarInd > .active').next('li');
listItem.find('.navLink').trigger('click', ['next']);
ScrollUp(0);
}
The problem is, for some reasons, when Nav Link 3 is active and I hit NEXT button - Instead of posting the form first via form.submit() - the nav 4 gets triggered - hence GET for nav 4 runs before form POST of nav 3.
My ValidateForm method is basically just checking if the form exists and is valid then Submit, else returns false. Its as below:
function ValidateForm(submit) {
var form = $('form');
// if form doesn't exist on the page - return true and continue
if (typeof form[0] === "undefined") return true;
// now check for any validation errors
if (submit) {
if (!$(form).valid()) {
return false;
} else {
$(form).submit();
}
}
else {
return true;
}
return true;
}
My speculation is that form.submit does get triggered as it should be but since submit takes a little longer to finish it continues with the next code block in the button onclick
event.
I first thought that this is a server side issue as in the POST I'm saving a big chunk of data with a few loops, and any code block that's process heavy I have that part in
var saveTask = Task.Factory.StartNew(() => ControllerHelper.SomeMethod(db, model)); Task.WaitAll(saveTask);
WaitAll will wait and pause the execution until SomeMethod finishes executing. I'm not sure how can I lock a process in JavaScript and wait for it to finish execution. Because I think If i can somehow lock the form.submit() in ValidateForm until its finished processing .. via a callback method perhaps...
Please if anyone can put me in right direction, I'd greatly appreciate the help. If you need more information please let me know I'd be happy to provide!
Ajax is async, and your forms submit which is using Ajax.BeginForm()
is using ajax. What is happening is that when you click your 'Next' button, which triggers the $('button').on('click', function () {
code:
- You call the
ValidateForm()
function (and assuming its valid),your$(form).submit();
line of code starts making a ajax POST - The code progresses to the final
return true;
line while the ajaxcall is executing. - Because the
ValidateForm()
function returned true, the$.ajax
GET call now starts, but at that point the ajax POST in theValidateForm()
function may not have finished executing causingyour GET method to return invalid data
You need to change your code so that the GET call is made once the POST method call has completed. And since your using the $.ajax()
methods throughout your code, and $.ajax()
gives you more flexibility, it seems unnecessary to use Ajax.BeginForm()
(and the extra overhead of including the jquery.unbtrusive-ajax.js
script). You should also be handling the forms .submit()
function (if you do not want the 'Next' button to be a submit button in the form, you could just trigger the .submit()
event in the buttons .click()
handler)
$(document).on('submit', 'form', function(e) {
e.preventDefault(); // cancel default submit
var form = $(this);
if (!form.valid()) {
return; // will display the validation errors
}
.... // get the relevant urls to the GET and POST methods etc
$.post(postUrl, form.serialize(), function(data) {
.... // not clear if your [HttpPost] method returns anything
}).done(function() {
$.get(getUrl, someData, function(response) {
.... // Update the DOM with the next form?
.... // Re-parse the validator for client side validation
}
}).fail(function() {
.... // code that you might run if the code in the [HttpPost] method fails
});
});
You should also consider returning the appropriate 'next' view in the [HttpPost]
method so that you don't then needs to make a second call back to the server to get it.
It is also worth reading the Deferred Object documentation and the use of $.when()
, $.then()
etc.
这篇关于等待 form.submit()/POST 完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!