【问题标题】:Page_ClientValidate prevents subsequent (unrelated) LinkButton client click from posting backPage_ClientValidate 防止后续(不相关的)LinkBut​​ton 客户端点击回发
【发布时间】:2018-07-10 11:51:33
【问题描述】:

TL;DR:如果在没有选择ListBox的情况下点击Edit按钮,触发验证失败,为什么Delete链接需要在之前点击两次浏览器有响应吗?

使用下面的源代码做一个非常简单的菜单,我有三个链接:

  1. New - 不执行验证并重定向到“创建”表单
  2. Edit - 检查是否选择了 ListItem,如果选择了,则重定向到“编辑”表单,并使用作为参数提供的 ID 值
  3. Delete - 重定向到“删除”页面,无论是否做出选择

我注意到一个奇怪的行为,并在这个简单的单页解决方案中重现 (download link from OneDrive):

  1. 加载页面
  2. 点击Edit而不从列表框中进行选择(出现验证错误)
  3. 点击Delete - 没有任何反应 - 为什么?
  4. 点击Delete - 服务器端重定向成功发生,但这应该发生在上面的第 3 步中

这是在我的浏览器中发生的情况(在 Chrome、FF 和 Edge 中测试) - 请注意,需要在删除链接上单击两次:

更新

问题似乎在 ASP.Net 的表单提交代码中:

function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {

    // On first click - this logic path is never reach, but on the second click, it is reached

        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

在我手动调用Page_ClientValidate() 之后,我试图弄清楚为什么if 语句返回false。

代码

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication1.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Test Page</title>
    <script>
        function RedirectToPageWithSelectedId() {
            if (Page_ClientValidate("mustSelect")) { // Asp.Net client-side validation, passing ValidationGroup as parameter
                var li = $("#<%=ListBox1.ClientID%> option:selected");
                var i = li.val();
                if (parseInt(i) != NaN) {
                    window.location.href = "/edit/" + i;
                }
            }
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <p>
                <asp:ListBox ID="ListBox1" runat="server" />
                <asp:RequiredFieldValidator ID="Rfv1" runat="server" Text="Selection required" ValidationGroup="mustSelect" ControlToValidate="ListBox1" Display="Dynamic" />
            </p>
            <ul>
                <li>
                    <asp:HyperLink ID="lnk_new" runat="server" Text="New" NavigateUrl="~/new" />
                </li>
                <li>
                    <asp:HyperLink ID="lnk_edit" runat="server" Text="Edit Selected" onclick="RedirectToPageWithSelectedId(); return false;" href="#" />
                </li>
                <li>
                    <asp:LinkButton ID="lnk_delete" runat="server" Text="Delete" />
                </li>
            </ul>
        </div>
    </form>
</body>
</html>

如果需要,我可以更新以提供页面的代码隐藏;问一下。

【问题讨论】:

  • 我无法重现该问题。一切正常。删除点击在第一次尝试时有效。
  • 你确定是先点击“编辑”,触发验证失败,然后然后在验证错误可见的时候删除?我可以在 FF、Chrome 和 Edge 中重现这个。
  • 啊,是的。然后我得到同样的结果。但是为什么只是为了检查 ListBox 是否有值而进行奇怪复杂的验证呢?您将RequiredFieldValidator 与与其他任何内容都不对应的验证组一起使用。触发验证的链接,旨在防止回发,而不是简单地打开链接。而你在搞乱Page_ClientValidate,为什么?
  • 我正在从 LinkBut​​tons 迁移到执行客户端重定向的 HyperLinks,以节省大量回发,但想使用已经存在的现有 .Net 验证的便利管道。我不想为了检查 ListItem 是否被选中而重新编写自己的自定义验证,并且正在努力寻找替代方法。
  • 我明白了,但验证器应该以这种方式使用。为什么不创建一个在更改时触发重定向的 DropDownList 而不是 ListBox ,您可以在其中选择多个无论如何都不会使用的值,因为只有第一项会获得重定向

标签: asp.net webforms asp.net-4.6


【解决方案1】:

在使用 Chrome 的调试器进行大量调查后,我发现了以下逻辑和解决方法。如果我弄错了,或者是否有更好的方法在客户端与 ASP.Net 的验证控件进行交互,请告诉我?

当单击 LinkBut​​ton 时,ASP.Net 会通过它的验证逻辑工作,即使该控件没有引起验证。这是让我吃惊的第一件事。在处理了验证逻辑之前,表单不会真正提交。

ASP.Net webforms 客户端验证

在验证期间,设置了两个页面级变量:

Page_IsValid

这是一个整体布尔值,指示所有验证器都是有效的。

Page_BlockSubmit

这通常持有与Page_IsValid相反的值,如果true,则阻止表单提交。

代码中的问题

当我的Edit 链接被点击时,我的代码触发了特定验证组的客户端验证:

Page_ClientValidate("mustSelect");

在该方法中,Page_IsValid 变为 false,因此 Page_BlockSubmit 变为 true。这可以防止表单在显示验证错误后执行任何操作,这是预期的。

问题出在 LinkBut​​ton 的点击事件上。按下时,无论该 LinkBut​​ton 是否正在执行验证,都会执行以下 JavaScript:

function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {

        // This is never reached if Page_BlockSubmit = true

        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

关键是之前的验证结果仍然保存在浏览器中,所以Page_BlockSubmit 仍然是true。这会导致上述方法跳过提交表单的内部代码,这意味着表单永远不会被提交。

奇怪的是,方法完成后,Page_IsValidPage_BlockSubmit 都被重置为其默认值。这就是页面在第二次编辑点击时提交的原因。

解决方法(或肮脏的破解?)

这感觉像是一个完整的 hack,但是通过在单击 LinkBut​​ton 时强制 Page_BlockSubmit 的值为 false,一切都按预期工作,并且无需手动开始重置 validation.isvalid 属性或隐藏消息。所有其他验证组/逻辑继续正常工作,但编辑链接现在可以正常工作:

<asp:LinkButton ID="lnk_delete" runat="server" Text="Delete" OnClientClick="Page_BlockSubmit = false;" />

再说一次,我不知道这是否会破坏 ASP.Net 的任何工作,并且希望能提供任何意见。

【讨论】:

    【解决方案2】:

    我会做这样的事情。给包含元素的&lt;div&gt; 一个类名,并按类绑定lnk_edit 的点击。现在您无需使用ClientID 即可查看是否选择了元素。这也适用于任意数量的 myContainer 副本,而无需添加更多 jQuery 代码。

    <div class="myContainer">
        <p>
            <asp:ListBox ID="ListBox1" runat="server" />
        </p>
        <ul>
            <li>
                <asp:HyperLink ID="lnk_new" runat="server" Text="New" NavigateUrl="~/new" />
            </li>
            <li>
                <asp:HyperLink ID="lnk_edit" runat="server" Text="Edit Selected" href="#" CssClass="myEditLink" />
                <span class="myEditLinkWarning"></span>
            </li>
            <li>
                <asp:LinkButton ID="lnk_delete" runat="server" Text="Delete" />
            </li>
        </ul>
    </div>
    
    <script type="text/javascript">
        $('.myContainer .myEditLink').click(function () {
            $(this).closest('div').find('select option').each(function () {
                if ($(this).prop('selected')) {
                    window.location.href = "/edit/" + $(this).val();
                }
            });
            $(this).closest('div').find('.myEditLinkWarning').html('Please select');
        });
    </script>
    

    【讨论】:

    • 谢谢。这就是我最终可能会做的事情,尽管我确实找到了一种解决方法,而无需进行自己的验证。网上有很多文档说明使用内置验证器的客户端验证很好,所以我不知道我的解决方法有多少“黑客”...
    猜你喜欢
    • 1970-01-01
    • 2019-09-24
    • 1970-01-01
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    相关资源
    最近更新 更多