Search code examples
asp.netuser-controlsupdatepanelpartial-postback

Javascript in a user control doesnt fire when partial postback occurs in .NET


I am trying to understand a bit about partial postbacks using updatepanels and while lots of things are stumping me, this one has me baffled.

I am using tabs inside of a UpdatePanel and becuase this is an data entry application, I am putting the content into user controls, so each tab has its own user control. In addition, I am adding custom javascript to each of the user controls to handle specific client side things for the stuff in that control.

In doing this though I have noticed that the javascript in the user control does not fire on partial page postbacks.

To simulate this I created a very simple app (using Master Pages with ScriptManager control on the master page) in VS2010.

The content panel of the default page has the following

<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>

Then I created a user control and simply added some plain text and a javascript alert

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

Then in the page load event of the Content page, I load the control into the panel.

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

In addition on the click handler of the button I also load the control (or a different control when toggling, as described further in)

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

So when the page is first loaded the alert fires, however when you click the button and the page is partially posted back, the alert never fires.

I took this a step forward and created a second user control and had the button toggle loading the controls and also added a normal button outside the UpdatePanel. Any time I did the full postback the javascript in the loaded user control fires, but it never fires on the partial postbacks.

I then went one step forward and moved my javascript in the controls into a function so I could call the function from the PageRequestManager, or from document.ready.

So now my two controls contain this script (with the alert containing a different message for each control)

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

And thusly I added this to the end of my content panel

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

Well now the javascript fires on every partial postback, however it fires the javascript for the control that last loaded by a full postback.

Example:

  1. Page intially loads UserControl1 - the alert for UserControl1 fires (not shown here - the userLoad function is being called in jquery document.ready)
  2. User clicks button - which should load UserControl2
  3. UserControl2 is loaded on the screen, but the alert for UserControl1 fires

A bit wordy, apologies for that, I found no easy way to explain this.

---Edit----

One of the things I have tried, working with Scotts suggestions. Keep the javascript for each control in a seperate file and put all of it into a function named something like function UC1Load() {} (maybe sticking a namespace around it, if I am feeling frisky), add a reference to the script file in the content page via a

<script src='UC1.js'> 

then register startup script to call this function

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

The biggest problem with this is that I am now having to download a bunch of script that wont ever be used, which is why I was putting the script in the user controls in the first place.


Solution

  • Ok, after creating my own test website and tinkering around, here's what I got to work:

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