等待form.submit() / POST完成

3
我被困在一个非常奇怪的情况下。这很复杂,但我会尽力解释。
问题的详细说明:
每次点击顶部导航(绿色甜甜圈/圆圈)或下一步按钮时,如果表单存在且有效,则必须提交表单。如果无效,则form.valid()会触发验证错误,返回false将停止任何进一步的传播。这个设置一直运行得很完美,直到我注意到了一个奇怪的行为,这种行为并不是非常持久。我的第三个选项卡上的表单具有相当多的数据。当我点击下一步按钮时,它应该实际上经过相同的过程:检查是否存在现有表单,如果有效,则提交。提交调用POST操作方法,当POST完成后,它GETs下一个选项卡的视图。它像这样工作5/10次,但其他时间GET在POST之前执行,这会导致下一页以不完整的数据加载。当我打断点进行调试时,我看到下一个选项卡的GET在当前选项卡的POST之前执行。
UI解释:
我有一个带有4个导航按钮的UI,顶部有<a> 按钮,在中心始终有一个表单,在底部有上一页和下一页按钮。

enter image description here

使用 Ajax.BeginForm 在MVC中构建表单。

对于顶部的每个导航链接 <a> 元素,我有一个JavaScript函数。

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);
        });
    }
}

这个函数在页面加载时绑定到每个顶部链接。
$('.navLinks').on('click', LoadTabs);

我的下一个和上一个按钮基本上触发点击事件,即LoadTabs函数。
$('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);
}

问题是,由于某些原因,当导航链接3处于活动状态并且我点击NEXT按钮时-而不是首先通过form.submit()提交表单-导航4被触发-因此在导航3的表单POST之前GET了导航4。
我的ValidateForm方法基本上只是检查表单是否存在且有效,然后提交,否则返回false。代码如下:
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;
}

我的猜测是,form.submit确实被触发了,但由于提交需要一些时间才能完成,它会继续执行button标签onclick事件中的下一个代码块。
起初我认为这是服务器端问题,因为在POST中,我保存了大量数据并进行了几个循环,任何处理重负载的代码块都在其中。
var saveTask = Task.Factory.StartNew(() => ControllerHelper.SomeMethod(db, model)); Task.WaitAll(saveTask);
WaitAll将等待并暂停执行,直到SomeMethod执行完成。我不确定如何在JavaScript中锁定进程并等待其执行完成。因为我认为,如果我可以通过回调方法在ValidateForm中锁定form.submit(),直到其完成处理...
请问是否有人能够指导我正确的方向,我将非常感激帮助。如果您需要更多信息,请告诉我,我很乐意提供!

为什么你在使用 Ajax.BeginForm(),而不是使用 $.ajax()。但问题在于 ajax 是异步的。当你调用 ValidateForm() 并且它有效时,一个 ajax 调用将被发送到 POST 方法。然后代码进入下一个 $.ajax() 调用到你的 GET 方法。它们同时发生,并不能保证哪个先完成。 - user3559349
我们以前没有这些验证逻辑,只是简单的ajax表单,将提交并更新页面响应(从操作方法返回的部分视图)。不过感谢您指出来。是的,我也这么想...我只是想了解是否有一种方法可以停止/暂停代码运行,直到POST完成... - Johny
如果表格可以按任意顺序完成,那么用户应该能够跳转到他们想要的任何表格,即使他们已经开始填写之前的表格(也许您只需在允许他们转到另一个表格之前显示确认)。 - user3559349
也许可以看一下这个:https://dev59.com/iGgu5IYBdhLWcg3wJT9I - Chris
@Chris 谢谢。我会看一下! - Johny
显示剩余8条评论
1个回答

7
Ajax是异步的,使用Ajax.BeginForm()的表单提交也在使用ajax。当您点击“下一页”按钮时,将触发以下代码:$('button').on('click', function () {

  1. 您调用ValidateForm()函数(假设其有效),您的$(form).submit();代码开始进行ajax POST
  2. 该代码进展到最终的return true;行,而ajax调用正在执行。
  3. 由于ValidateForm()函数返回了true,因此$.ajax GET调用现在开始运行,但在那时,ValidateForm()函数中的ajax POST可能尚未完成执行,导致您的GET方法返回无效数据。
你需要修改代码,使得GET调用在POST方法调用完成后才执行。由于你在整个代码中都在使用$.ajax()方法,并且$.ajax()提供了更多的灵活性,因此似乎没有必要使用Ajax.BeginForm()(以及包含jquery.unbtrusive-ajax.js脚本的额外开销)。你还应该处理表单的.submit()函数(如果你不希望“Next”按钮成为表单中的提交按钮,你可以在按钮的.click()处理程序中触发.submit()事件)。
$(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
    });
});

您还应该考虑在[HttpPost]方法中返回适当的“下一个”视图,以便您不需要再次调用服务器来获取它。

同时,值得阅读延迟对象文档$.when()$.then()等的使用。


我最终使用了延迟对象。如果表单存在且有效,则返回 $.post(),否则返回 deferred.resolve.promise()。我在 $.then() 的回调函数中进行了 $.get 调用。我认为这解决了我的问题。感谢您抽出时间来帮助我。 - Johny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接