【问题标题】:Javascript in a user control doesnt fire when partial postback occurs in .NET当 .NET 中发生部分回发时,用户控件中的 Javascript 不会触发
【发布时间】:2012-04-20 10:37:51
【问题描述】:

我正在尝试了解一些关于使用更新面板的部分回发的信息,虽然很多事情让我感到困惑,但这个让我感到困惑。

我在 UpdatePanel 中使用选项卡,因为这是一个数据输入应用程序,我将内容放入用户控件中,因此每个选项卡都有自己的用户控件。此外,我正在向每个用户控件添加自定义 javascript,以处理该控件中内容的特定客户端事务。

在执行此操作时,我注意到用户控件中的 javascript 不会在部分页面回发时触发。

为了模拟这一点,我在 VS2010 中创建了一个非常简单的应用程序(在母版页上使用带有 ScriptManager 控件的母版页)。

默认页面的内容面板有以下内容

<asp:UpdatePanel runat="server" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
   <asp:Button runat="server" ID="Button1" Text="Partial postback" />
   <br />
   <asp:Panel runat="server" ID="panel"></asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>

然后我创建了一个用户控件并简单地添加了一些纯文本和一个 javascript 警报

<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="UC1.ascx.vb" Inherits="TestWebApp_VB.UC1" %>
This is user control 1
<script> alert('control 1'); </script>

然后在Content页面的页面加载事件中,我将控件加载到面板中。

   Panel.Controls.Add(LoadControl("UC1.ascx"))

此外,在按钮的点击处理程序上,我还加载了控件(或在切换时加载不同的控件,详见下文)

Button_Click
 Panel.Controls.Clear()
 Panel.Controls.Add(LoadControl("SomeControl.ascx"))

因此,当页面首次加载时会触发警报,但是当您单击按钮并且页面被部分回发时,警报永远不会触发。

我向前迈了一步,创建了第二个用户控件,并让按钮切换加载控件,并在 UpdatePanel 之外添加了一个普通按钮。每当我进行完整回发时,加载的用户控件中的 javascript 都会触发,但它不会在部分回发时触发。

然后我向前迈了一步,将控件中的 javascript 移到一个函数中,这样我就可以从 PageRequestManager 或 document.ready 调用该函数。

所以现在我的两个控件包含这个脚本(警报包含每个控件的不同消息)

<script type="text/javascript">
  function userLoad() {
    alert("Identify control");
  }
</script>

因此我将其添加到内容面板的末尾

<script type="text/javascript">
   var prm = Sys.WebForms.PageRequestManager.getInstance().add_endRequest(userLoad);
</script>

现在,javascript 会在每个部分回发时触发,但是它会触发上次由完整回发加载的控件的 javascript。

例子:

  1. 页面最初加载 UserControl1 - 触发 UserControl1 的警报 (此处未显示 - 在 jquery 中调用了 userLoad 函数 document.ready)
  2. 用户单击按钮 - 应加载 UserControl2
  3. UserControl2 已加载到屏幕上,但 UserControl1 的警报 火灾

有点罗嗦,抱歉,我找不到简单的方法来解释这一点。

---编辑----

我尝试过的其中一件事是与 Scotts 的建议一起工作。 将每个控件的 javascript 保存在一个单独的文件中,并将其全部放入一个名为 function UC1Load() {} 之类的函数中(如果我觉得很活泼,可能会在它周围贴上一个命名空间),添加对脚本文件的引用内容页面通过一个

<script src='UC1.js'> 

然后注册启动脚本来调用这个函数

 ScriptManager.RegisterStartupScript(ctrl, ctrl.GetType(), "UserJS", "UC1Load()", True)

最大的问题是我现在必须下载一堆永远不会使用的脚本,这就是我首先将脚本放在用户控件中的原因。

【问题讨论】:

  • UserControl1 和 UserControl2 是同时出现在页面上还是 UesrControl2 取代了 UserControl1?
  • 代码设置为在任何给定的回发中只加载一个用户控件。
  • 如果您的 js 函数与它们所在的控件有关,并且您已将它们移动到每个相应的控件中(希望在所有内容之后的最底部),为什么不让它们内联触发呢?
  • 内联脚本不会在部分回发时触发。请参阅问题中的初始测试用例 - 只有一个内联警报的控件。警报仅在完整回发时触发。这让我很困惑!
  • 这个问题已经包含了我的问题的解决方案。问得好,先生。

标签: asp.net user-controls updatepanel partial-postback


【解决方案1】:

好的,在创建了我自己的测试网站并进行了修改之后,这就是我要工作的内容:

TestControl1

<%@ Control Language="C#" 
            AutoEventWireup="true" 
            CodeFile="TestControl1.ascx.cs" 
            Inherits="Controls_TestControl1" %>

This is Test Control 1

TestControl2

<%@ Control Language="C#" 
            AutoEventWireup="true" 
            CodeFile="TestControl2.ascx.cs" 
            Inherits="Controls_TestControl2" %>

This is Test Control 2

10049777.aspx

<%@ Page Language="C#" 
         MasterPageFile="~/Site.master" 
         AutoEventWireup="true" 
         CodeFile="10049777.aspx.cs" 
         Inherits="_10049777" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="Server" />
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="Server">
    <asp:UpdatePanel ID="UpdatePanel1" runat="server" 
        UpdateMode="Conditional" 
        ChildrenAsTriggers="true">
        <ContentTemplate>
            <asp:Button ID="Button1" runat="server"
                Text="Partial postback" 
                OnClick="Button1_Click" />
            <br /><br />
            <asp:Panel runat="server" ID="Panel1" />
        </ContentTemplate>
    </asp:UpdatePanel>
</asp:Content>

10049777.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        var TestControl1 = LoadControl("Controls\\TestControl1.ascx");

        Panel1.Controls.Add(TestControl1);
        ScriptManager.RegisterStartupScript(TestControl1, 
                                            TestControl1.GetType(), 
                                            "TestControl1Script", 
                                            "alert(\"control 1\");", 
                                            true);
    }
}

protected void Button1_Click(object sender, EventArgs e)
{
    var TestControl2 = LoadControl("Controls\\TestControl2.ascx");

    Panel1.Controls.Clear();
    Panel1.Controls.Add(TestControl2);

    ScriptManager.RegisterStartupScript(TestControl2, 
                                        TestControl2.GetType(), 
                                        "TestControl2Script", 
                                        "alert(\"control 2\");", 
                                        true);
}

【讨论】:

  • 这似乎有效。但是问题的症结对我来说仍然不清楚,为什么用户控件内部的 javascript 不运行,或者当异步加载不同的用户控件时,请求管理器仍然看到原始控件 javascript。
  • 另外,这种方式在这个测试中使用一些简单的 javascript 效果很好,但实际上,我将有 50 多行代码,我宁愿不把它们塞进字符串变量中,因为我失去了所有调试和可维护性。不过感谢您的时间,我一直在用头撞墙试图弄清楚。
  • 我认为这是由于 ASP.NET 如何处理动态添加到 UpdatePanel 的控件。 This question 展示了对这个问题的更多见解,可能对您有所帮助。
  • 我听说过调试/可维护性。在我看来,我将如何处理这就是我个人如何做我自己的 JavaScript/jQuery。我有一个包含我所有代码的 script.js,根据我所在的页面,该页面的 init() 会触发绑定我需要的所有内容。这是Paul Irish's Markup Based Unobtrusive Comprehensive Dom Ready Execution post 的衍生产品。
  • @MikeKiemen 如果您不介意对此表示赞同,因为您确实发现它非常有用,可以在您编辑的问题中实施。 Upvotes 不仅仅是为了完成正确的答案,而是为了有用的答案,并奖励他们在你的问题上花费的时间。我确实在我发布的 2 个答案之间花了几个小时。将不胜感激。 =D
【解决方案2】:

您可以采用的另一种方法是使用 jQuery 的 .on() 方法。

当提供选择器时,事件处理程序被称为 委托。事件直接发生时不调用处理程序 绑定元素,但仅适用于后代(内部元素) 匹配选择器。 jQuery 将事件从事件目标向上冒泡 到处理程序附加的元素(即,最里面 最外层元素)并运行该处理程序的任何元素 路径匹配选择器。

事件处理程序仅绑定到当前选定的元素;他们 在您的代码调用 .on() 时必须存在于页面上。

...

委托事件的优势在于它们可以处理来自 以后添加到文档中的后代元素。经过 选择一个保证在该时间存在的元素 附加了委托事件处理程序,您可以使用委托事件 避免频繁附加和删除事件处理程序的需要。这 element 可以是视图中的容器元素 例如,模型-视图-控制器设计,或记录事件 处理程序想要监视文档中的所有冒泡事件。这 文档元素在文档头部之前可用 加载任何其他 HTML,因此在此处附加事件是安全的 等待文档准备好。

因此,例如,您可以使用相同的类将 UserControls 与 div 一起包装,如下所示:

用户控件 1

<div class="userControl">
    <span>This is UserControl 1</span>
</div>

用户控件 2

<div class="userControl">
    <span>This is UserControl 2</span>
</div>

然后,在您的 MasterPage 中,在您的 &lt;/body&gt; 标记之前的最底部,包含您的 jQuery 引用和您的 jQuery 脚本。在我的示例中,当单击 &lt;span /&gt; 时,我会在 UserControl 中打印出 &lt;span /&gt; 的内容。

jQuery

​​>
    <script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js"></script>
    <script>
        $("div[id$=UpdatePanel1]")
            .on("click", "div.userControl span", function() { 
                alert($(this).html().trim()); 
            });
    </script>
</body>
</html>

【讨论】:

    【解决方案3】:

    为内联脚本问题找到了一个很好的解释

    http://veskokolev.blogspot.com/2009/03/inline-scripts-in-updatepanel-doesnt.html

    简而言之:部分回发会更新更新面板的 innerHtml,因此它将 html 的 blob 视为纯文本,因此不会在 html 中注册任何脚本标签。

    所以我的最终解决方案是这样的。

    每个控件都有一个自己的 javascript 文件,其函数名称类似于

    function controlNameLoad() { ..do your stuff }
    

    在内容面板代码内(在判断加载哪个控件的case语句内)

    dim ctrl = LoadControl("UCName.ascx")
    panel.Controls.Add(ctrl)
    ScriptManager.RegisterClientScriptInclude(ctrl, ctrl.GetType(), "UserJS", "UCName.js")
    ScriptManager.RegisterStartupScript(ctrl, ctrl.GetType(), "UserJS", "controlNameLoad()", True)
    

    这适用于我的测试项目,稍后会看到我是否可以在我的真实代码中实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      相关资源
      最近更新 更多