Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

rousso's .net blog

the .net blog of Yannis Roussochatzakis
Multiple calls to RegisterOnSubmitStatement and Client-Side Validation

Ok! Here is a new thing I discovered yet again the hard way...

In short: Do not call Page.ClientScript.RegisterOnSubmitStatement after the Page Load event.

(What?!)

Well yes! It's not under all circumstances that you can notice the difference but it's there and it's major!

I do not really wan to describe this, so I 'll take you though it with an example:

Let's say you have an aspx page. The page has two controls in it. For simplicity lets make those controls UserControls. The controls are pretty simple: just a TextBox and a RequiredFieldValidator in each of them.

So there you have it:

Control A (let's call it OnSubmitControlA):

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="OnSubmitControlA.ascx.cs" Inherits="OnSubmitControlA" %>
<asp:TextBox ID="TextBox1" runat="server" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1" ErrorMessage="RequiredFieldValidator" />

and the code file:

public partial class OnSubmitControlA : System.Web.UI.UserControl
{
   protected override void OnPreRender(EventArgs e)
   {
      Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "DoingSomething", "alert('This alert is registered by OnSubmitControlA');");
      base.OnPreRender(e);
   }
}

Control B (let's call it OnSubmitControlB):

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="OnSubmitControlB.ascx.cs" Inherits="OnSubmitControlB" %>
<asp:TextBox ID="TextBox1" runat="server" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1" ErrorMessage="RequiredFieldValidator" />

and the code file:

public partial class OnSubmitControlB : System.Web.UI.UserControl
{
   protected override void OnPreRender(EventArgs e)
   {
      Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "DoingSomethingElse", "alert('This alert is registered by OnSubmitControlB');");
      base.OnPreRender(e);
   }
}

And finally the page itself:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="OnSubmitTest.aspx.cs" Inherits="OnSubmitTest" %>
<%@ Register src="OnSubmitControlA.ascx" TagName="OnSubmitControlA" TagPrefix="uc1" %>
<%@ Register src="OnSubmitControlB.ascx" TagName="OnSubmitControlB" TagPrefix="uc2" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
   <head runat="server">
      <title>Test Page</title>
   </head>
   <body>
      <form id="form1" runat="server">
         <uc1:OnSubmitControlA ID="OnSubmitControlA1" runat="server" />
         <uc2:OnSubmitControlB ID="OnSubmitControlB1" runat="server" />
         <asp:Button ID="Button1" runat="server" Text="Button" />
      </form>
   </body>
</html>

(the codefile has nothing special in it...)

The page has of course a submit button so that we can submit and test it...

So! What we have here!?

  • A Page,
  • two controls that wan to access client-side code just before the page submits (for no particular reason)
  • and at least a Validator Control that will fail validation at some point. (If we did not have a validator then I would not have a case here!)

Now go render the page an see the result. If you leave either TextBox empty and click on the submit button, you will notice that only the alert from the first control pop's up. The other registered script is never called....

Now go back and make a slight change. Move in both contols' codefile the call to Page.ClientScript.RegisterOnSubmitStatement from OnPreRender to OnLoad, like this:

public partial class OnSubmitControlA : System.Web.UI.UserControl
{ 
   protected override void OnLoad(EventArgs e)
   {
      base.OnLoad(e);
      Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "DoingSomething", "alert('This alert is registered by OnSubmitControlA');");
   }
   protected override void OnPreRender(EventArgs e)
   {
      // Let's comment this out
      // Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "DoingSomething", "alert('This alert is registered by OnSubmitControlA');");
      base.OnPreRender(e);
   }
}

do the same on the other control:

public partial class OnSubmitControlB : System.Web.UI.UserControl
{
   protected override void OnLoad(EventArgs e)
   {
      base.OnLoad(e);
      Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "DoingSomethingElse", "alert('This alert is registered by OnSubmitControlB');");
   }
   protected override void OnPreRender(EventArgs e)
   {
      // Let's comment this out also
      // Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "DoingSomethingElse", "alert('This alert is registered by OnSubmitControlB');");
      base.OnPreRender(e);
   }
}

Done! Go back and render the page! Leave either TextBox empty and click submit... See??? Now both alerts pop up!!!

Why is that???

Well look at the source of the rendered page before and after the change to see what' going on:

Here is the script rendered when the call to RegisterOnSubmitStatement is placed in the OnPreRender event:

<script type="text/javascript">
<!--
   function WebForm_OnSubmit() {
      alert('This is registered by OnSubmitControlA');
      if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) 
         return false;
      alert('This is registered by OnSubmitControlB');
      return true;
   }
// -->
</script>

And here is the script rendered when the call to RegisterOnSubmitStatement is placed in the OnLoad event:

<script type="text/javascript">
<!--
   function WebForm_OnSubmit() {
      alert('This is registered by OnSubmitControlA');
      alert('This is registered by OnSubmitControlB');
      if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) 
         return false;
      return true;
   }
// -->
</script>

Got it?

If RegisterOnSubmitStatement is called after OnLoad, then the first time it's called the framework appends the statement that calls ValidatorOnSubmit() and returns false if it fails; (effectively blocking the rest of the script from executing). Subsequent calls of RegisterOnSubmitStatement (after OnLoad) are appended to the script generated by the first call (and get blocked by the effect I just described).

Instead, if all your calls to RegisterOnSubmitStatement come before the end of the OnLoad phase, then all registered scripts are appended to previously generated scripts before the eventual injection of the call to ValidatorOnSubmit().

Hoping for comments on this...

Share
Posted: Σάββατο, 2 Δεκεμβρίου 2006 9:45 μμ από το μέλος rousso
Δημοσίευση στην κατηγορία:

Σχόλια:

Χωρίς Σχόλια

Έχει απενεργοποιηθεί η προσθήκη σχολίων από ανώνυμα μέλη