ASP.NET Client-Server Interaction
"Halt App Until JavaScript Confirm Dialog Clicked". "JavaScript in code-behind problem". "How to fire ASP.NET code from JavaScript function". "Confirm delete". These are all titles of recent questions posted in various .NET forums around the web. In each case, the question poser had a fundamental misunderstanding of the client-server nature of ASP.NET.
ASP.NET is a web programming language. The web is based on the request-response model. The client makes a request, and the server generates a response. In the above examples, the coders had failed to keep this in mind, and so major confusion ensued. In a web application, code can run on the client, via JavaScript, or on the server, via C# or VB.NET code-behind. ASP.NET confuses the issue by making it appear that server code is running in response to client actions (which isn't true; server code only runs in response to a form submit). So it seems natural that, for example, a code-behind function could make an "Alert" or "Please confirm" dialog appear in the browser, wait for a response, and then continue running the server code.
This article will offer some techniques for common issues with ASP.NET client-server interaction. It would be helpful to review my other two articles, ASP.NET and HTML and ASP.NET ViewState for background on ASP.NET.
Display a "Thank You" Message
After a page is submitted and processed, you may wish to display a JavaScript message. You may want to thank the user, or simply confirm that their information was correctly posted. Or, you may have found a problem with the supplied data, and need to display a warning message. This scenario could be described as wanting a JavaScript "onload" script to run on PostBack, but not to run the first time the page loads.
For this situation, use the "RegisterStartupScript" method of the "Page" class. For this example, we'll use the same sample application as in the previous articles:
We'll add some code to the button's server-side click event.
private void Button1_Click(object sender, System.EventArgs e) { Page.RegisterStartupScript(@"startup",@"<script>alert('Thank you!');</script>"); }
The method takes two arguments, both strings. The first string is a "key". It's just a name that you can use, server-side, to refer to the script (for example, with the "IsStartupScriptRegistered" method). The second string is the script itself. Note that you must include the script tags. This is because the RegisterStartupScript method is extremely simplistic. It adds the entire contents of the second string to the end of the page's form. If you fail to provide the script tags, you'll just end up displaying text or HTML.
While it is fine to think of this as "displaying an alert when the user clicks a button", that isn't quite what is happening. For example, the button could display an alert through JavaScript, without ever making a trip back to the server.
A breakdown of what we're really doing: [1] user requests a page [2] server generates a page [3] user clicks a button [4] the page submits the form [5] the server "unwraps" the form data, HTTP Headers, and ViewState data to generate the "Button1_Click" event [6] the event handler runs, which writes a script to the page [7] the page is served back to the user [8] the script runs, displaying our "Thank you!" message. The key lesson is that we can author client-side scripts in response to events on the server.
Integrating a "Confirm" Dialog
Another common source of confusion is how best to integrate a client-side "Confirm" message with server-side form processing. Put another way, how can you make a button or link run both a client event and, conditionally, a server event?
For example, the user clicks a button to delete a record from the database. You've already written the server code and attached it to the button. Ideally, though, you want to present a dialog box asking the user if they really want to delete this record. If they answer "Yes", the browser should submit the page to the server, where the "delete" code will run. If they answer "No", then the form submission should be cancelled.
First, a short tutorial on HTML Forms and buttons. When a button is clicked, it generates a "click" event. What happens when the button is clicked depends on what is defined in the button's "onclick" handler. Consider this simple example:
<input type="button" value="Click me!" onclick="alert('You clicked me.');" />
In the above example, the code to handle the click event is directly included, inline. However, it could also be contained in a JavaScript function, in which case our onclick handler would need to call the function, like so: onclick="myFunction();".
One key characteristic of HTML buttons is that the click event's function can be cancelled, if the function returns a "false" boolean.
<input type="button" value="Click me!" onclick="return; alert('You clicked me.');" />
Clicking this version of our button does nothing. This is because the onclick handler "returns", before any other code runs. The rest of the code is effectively "cancelled" by the return. Optionally, the "return" keyword could pass back a boolean. If the boolean is "false", the button's default action is cancelled. This doesn't make much sense in the case of regular buttons. But in our scenario, we're not dealing with a regular button. We're dealing with a submit button. Returning a "false" would cancel the form submission. Let's put the pieces together. Consider the following code:
<HTML> <head> <script type="text/javascript"> function myFunction() { return false; } </script> </head> <body> <form type="POST" action="myPage.aspx"> <input type="text" id="myTextBox" /> <input type="submit" onclick="return myFunction();"> </form> </body> </HTML>
Hopefully you can see that this form will never submit.
JavaScript confirm() and ASP.NET
It would be tempting then, to write a JavaScript function that displays a confirm, and if the user clicks "Yes", return "true". If they click "No", return "false". Then we'd add an "onclick" handler to our button, and we're in business. That's almost right. Remember that an ASP.NET Server Control is not HTML. It produces, or emits, HTML. If we add an "onclick" handler to an ASP Button Server Control, ASP.NET assumes we're adding a server-side event handler, not a client-side event handler.
Let's look at our sample project in Visual Studio's "HTML" view (reformatted slightly to fit this article's stylesheet). You can see that I've already added the "confirm" script. Near the bottom, you'll see our Button Server Control. If we tried to add onclick="return confirmDelete();" to the button declaration, we'd get errors when we tried to run the project. Why? Because an ASP.NET Button server control's "onclick" attribute expects you to provide a server-side function. This Button isn't an HTML submit button. It produces an HTML submit button when it runs.
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="testsuite.WebForm1" enableViewState="True"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD> <title>WebForm1</title> <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name="vs_defaultClientScript" content="JavaScript"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> <script type="text/javascript"> function confirmDelete() { var x = confirm("Are you sure you want to do this?"); if (x) { return true; } else { alert("Delete cancelled."); return false; } } </script> </HEAD> <body MS_POSITIONING="GridLayout"> <form id="Form1" method="post" runat="server"> <asp:TextBox id="TextBox1" style="Z-INDEX: 101; LEFT: 96px; POSITION: absolute; TOP: 32px" runat="server"></asp:TextBox> <asp:Label id="Label1" style="Z-INDEX: 102; LEFT: 40px; POSITION: absolute; TOP: 32px" runat="server">Label</asp:Label> <asp:Button id="Button1" style="Z-INDEX: 103; LEFT: 192px; POSITION: absolute; TOP: 72px" runat="server" Text="Button"></asp:Button> </form> </body> </HTML>
What we have to do is add the client-side attribute to the button at run-time. We can do this by using the "Add" method of the control's "Attributes" collection.
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Text; using System.IO; namespace testsuite { ////// Summary description for WebForm1. /// public class WebForm1 : System.Web.UI.Page { protected System.Web.UI.WebControls.TextBox TextBox1; protected System.Web.UI.WebControls.Label Label1; protected System.Web.UI.WebControls.Button Button1; private void Page_Load(object sender, System.EventArgs e) { // Put user code to initialize the page here Button1.Attributes.Add("onclick","return confirmDelete();"); } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { // // CODEGEN: This call is required by the ASP.NET Web Form Designer. // InitializeComponent(); base.OnInit(e); } ////// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.Button1.Click += new System.EventHandler(this.Button1_Click); this.Load += new System.EventHandler(this.Page_Load); } #endregion private void Button1_Click(object sender, System.EventArgs e) { Page.RegisterStartupScript(@"startup",@"<script>alert('Record deleted.');</script>"); } } }