Registering Scripts

Techniques to Add Scripts Programmatically to ASP.NET Pages

CoreCoder

LANGUAGES: C#

ASP.NET VERSIONS: 2.0

 

Registering Scripts

Techniques to Add Scripts Programmatically to ASP.NET Pages

 

By Dino Esposito

 

ASP.NET pages are essentially server-side pages that perform most of their tasks on the server. These tasks are typically coded using managed languages and require the configuration of server controls so they can generate and send the expected markup to the browser. But what if you need to generate some client-side script code, as well? At the end of the day, script code is simply plain text wrapped by a proper <script> tag. The JavaScript code may not be static and constant; instead, more often than not, it is subject to dynamically-defined control IDs or values determined only during the processing of the current request so you may need to accumulate the script in a buffer and send it out to the browser when you feel it s OK.

 

In this article, I ll review some of the techniques you can employ in ASP.NET to embed dynamically-generated script code in ASP.NET pages. I ll also take a look at ways of doing it in the context of AJAX postbacks.

 

The <script> Tag

The <script> tag is the official way of inserting script code in any Web page. The tag delimits the portion of the downloaded page that refers to the script code. The real code you need to insert can be linked from an external URL through the src attribute, or written inline in the body of the tag.

 

More importantly, the <script> tag is the only low-level tool available in HTML to link or embed script code. This means that any other approach I ll be discussing in the remainder of the article will be implemented in terms of one or more <script> tags. If the concrete technique looks different or simpler to implement, be aware that it is only a matter of abstraction that some framework adds to make your life simpler.

 

The <script> tag is helpful if you have a static JavaScript file or an inline script to download. An inline script makes the page a bit larger each time it is downloaded. A linked script can be cached by the browser, thus saving an additional download each time the page is requested. This is also an excellent choice if multiple pages share the same script.

 

But what if the script code is not the same for each request? Or what if it has fragments whose details (i.e., variable names, element IDs, styles) are figured out during each request? In this case, you can link the <script> tag to the URL of an HTTP handler and let this run-time ASP.NET component do the work for you. The HTTP handler will figure out the run-time conditions and determine which file to download or which updates are necessary to a script template to make it suitable for download. If you use a centralized HTTP handler, you can make it implement any logic you need to generate the script to download. Predefined script templates are a common trick to define a base of code to extend and complete dynamically. Here s the skeleton of such an HTTP handler:

 

public void ProcessRequest(HttpContext context)

{

    // Load the script template

     string scriptTemplate = LoadScriptTemplate(...);

    // Replace placeholders in the template

    string js = ReplacePlaceHolders(scriptTemplate);

    // Output JavaScript to the client

    context.Response.ContentType = "text/JavaScript";

    context.Response.Write(js);

    context.Response.End();

}

 

The predefined script templates are local application resources and can either be deployed as local resources or embedded in some application assemblies. Using the <script> tag explicitly and linking it to the URL of an HTTP handler delivers the greatest flexibility, and gives you full control over what happens but at the same time, it requires some work of your own. For example, ASP.NET AJAX Extensions allows you to register external script files through the ScriptManager control. When you do so, it actually emits <script> tags linked to a system-defined HTTP handler named ScriptResource.axd. The handler provides a number of additional script-related services. For example, it manages localization of scripts and chooses between debug and release versions of them. This is a perfect example of the power that HTTP handlers deliver when used in the context of script inclusion in pages.

 

Composing the Script Programmatically

An HTTP handler is perhaps overkill, when all that you need is to insert a short piece of script that simply provides a control-specific functionality, such as validating the contents of a textbox or collapsing and expanding an HTML panel. Quite a few ASP.NET built-in controls do insert some script in client pages. However, they do it using inline blocks of JavaScript code that is composed and emitted during the page rendering.

 

In this case, you accumulate the text in a memory buffer, then output the buffer to the response stream. What s the most effective tool in which to accumulate text? In general, any string manipulation API would work. However, in the .NET Framework, the StringBuilder class is the preferred tool to compose strings dynamically. The reason is that .NET strings are immutable by nature, so when two strings are going to be concatenated (a very common task when you create control-specific scripts), what really happens is that a new string is created whose contents is the concatenation of the other two. Clearly, this behavior is less than optimal and leads to performance bottlenecks. The StringBuilder class features an easy-to-understand programming interface and, more importantly, manages an internal buffer of memory where text is appended as raw bytes. A final string is obtained only through a call to the ToString method.

 

What s the point here? Quite simply, you must use the programming interface of a general-purpose string manipulation class like StringBuilder in order to build a very special type of text such as a JavaScript program. Here s an example:

 

StringBuilder builder = new StringBuilder();

builder.Append("<script type=\"text/javascript\">");

builder.Append("function Foo() {");

builder.Append(" // do something");

builder.Append("}");

builder.Append("</script>");

string js = builder.ToString();

 

The more complex the script, the more error prone this code turns out to be (as well as difficult to maintain and edit). A better approach might be using local templates of scripts with placeholders that you load into a string and then expand using the String.Format method:

 

string scriptTemplate = LoadScriptTemplate(...);

string js = String.Format(scriptTemplate, functionName,

 param1, param2, ...);

 

If you need to emit a lot of dynamically-generated script code in your pages, you might want to consider writing an ad hoc JavaScript builder class to simplify your task and come up with a more readable and less error-prone code than above. The JavaScript builder class will feature a programming interface similar to StringBuilder, except that it supplies ad hoc methods to add functions, variable declarations, and other JavaScript-typical constructs.

 

Let s assume you get your script done and saved in some memory variable. How can you have it embedded in the output stream being flushed to the browser?

 

Using the Page Class Facilities

ASP.NET supports a subscription pattern for developers to publish their script code in the body of a Web page. In short, you have two main options: adding an explicit <script> tag pointing to a URL or composing your script code and registering it with the page infrastructure for later output. The table in Figure 1 lists all the methods in the ClientScriptManager class that pertain to the insertion of script code in the client page.

 

Method

Description

IsClientScriptBlockRegistered

Determines whether the specified client script is registered with the page.

IsClientScriptIncludeRegistered

Determines whether the specified script-include element is registered with the page.

IsOnSubmitStatementRegistered

Determines whether the specified client submit script is registered with the page.

IsStartupScriptRegistered

Determines whether the specified client startup script is registered with the page.

RegisterArrayDeclaration

Use this method to add an ECMAScript array to the client page. This method accepts the name of the array and a string that will be used verbatim as the body of the array. For example, if you call the method with arguments such as theArray and a , b , you get the following JavaScript code:

 

var theArray = new Array( a , b );

RegisterClientScriptBlock

Emits client-side script blocks in the client page just after the opening tag of the HTML <form> element and before any server controls.

RegisterClientScriptInclude

Emits the markup to import an external script file through the src attribute of the <script> tag.

RegisterClientScriptResource

Calls into RegisterClientScriptInclude to register an external script file except that in this case, the external file is taken from the page s resources.

RegisterExpandoAttribute

Emits the markup to import a custom, non-standard attribute.

RegisterHiddenField

Emits the markup required for a hidden field.

RegisterOnSubmitStatement

Emits client-side script associated with the form s OnSubmit event.

RegisterStartupScript

Emits client-side script at the end of the <form> tag so that it can be run at startup when all controls have been fully initialized.

Figure 1: Methods to manipulate script in ASP.NET pages.

 

In ASP.NET 1.x, the Page class featured most of the methods shown in Figure 1. These methods have been marked as obsolete in ASP.NET 2.0 and replaced by analogous methods in the ClientScriptManager class. In ASP.NET 2.0, methods to emit script on demand are grouped under the ClientScript property of the Page class. The ClientScript property is an instance of a helper class named ClientScriptManager. You use the methods shown in Figure 1 to inject script code in client pages based on run-time conditions detected on the server. Here s an example:

 

this.ClientScript.RegisterOnSubmitStatement(this.GetType(),

   "MySubmit", js);

 

In particular, the RegisterOnSubmitStatement method includes script code to be run each time the DOM form s onsubmit event fires on the client. Let s briefly review the signature of the RegisterOnSubmitStatement method:

 

public void RegisterOnSubmitStatement (

   Type type, string key, string script)

 

The first two arguments, type and key, uniquely identify a submit script statement. Script statements with the same key and type are treated as duplicates, and any attempt to create a duplicate statement is ignored. The third argument is a string of JavaScript code that will provide the body of the statement and the code that actually executes. The type parameter is usually set to the type of the current page or the current control if you re using this code from within a server control. The key argument is a moniker to identify the piece of script you re registering. Other RegisterXXX methods in Figure 1 have a similar signature.

 

RegisterXXX methods register a piece of code with the page so that the script will be emitted in the response when the page renders out. All IsXXX methods check whether a given piece of script is already registered with the page to avoid duplication and, subsequently, client-page errors.

 

What really happens when you register a script with the client script manager? First off, the script code is not immediately sent to the client. Instead, it is cached in an internal memory structure and kept warm for further inclusion in the output stream. All RegisterXXX methods have an additional Boolean parameter to indicate whether or not the script should include a wrapper <script> tag. You don t want the framework to include the tag if the script you registered contains it already.

 

As you can see from Figure 1, there are various scenarios addressed. For example, any code registered through RegisterClientScriptBlock appears inside the <form> tag following the viewstate hidden field, but preceding anything else. It represents a piece of script callable on demand. A startup script block is a piece of script code that executes when the page has been fully initialized, but before it is displayed to the user. This startup <script> block is inserted immediately before the closing </form> tag. You can also link the page to an external script file using the ClientScriptManager facilities. The result is the same as having a static <script> tag. You can also load a script file from the resources of the page assembly; start by adding a script file (say, CustomScript.js) to the project as an embedded resource. Next, link the code to the page using the RegisterClientScriptResource method:

 

ClientScript.RegisterClientScriptResource(this.GetType(),

   "CustomScript.js");

 

The markup of the page ends up containing a <script> element where a system HTTP handler is used to load the specified script from the resources of an assembly.

 

Registering Scripts in AJAX Postbacks

Script registration is an old feature of ASP.NET, in spite of the slight changes that occurred in the transition from version 1.x to 2.0. To most developers, it is pretty neat and clear. However, the advent of ASP.NET AJAX Extensions mixed things up a little bit. The ScriptManager control, in fact, features a bunch of similar RegisterXXX methods. What are they for? And which methods should you be using?

 

First off, ClientScriptManager s and ScriptManager s registration methods serve the same purpose, but in radically different scenarios. You need to use the ScriptManager s methods only if you need to emit script code during an AJAX postback.

 

An AJAX partial-rendering postback is processed by the runtime as usual, except for the rendering stage. At this time, the markup is generated and any registered script is emitted. During AJAX postbacks, the ScriptManager is responsible for the markup rendering, so it s the ScriptManager that needs to know about registered scripts to emit. If you use ClientScriptMananager s methods in an AJAX page, you run the risk that no script will be emitted during the refresh of a panel. As a result, a portion of your page may experience strange behaviors.

 

Conclusion

Script code is essential to ASP.NET pages, not only AJAX-powered pages. In many cases, the script you need can be statically determined and can be linked directly through its URL. In other cases, you instead need to compose the script programmatically, retrieve it from a storage medium, and load it from resources. Once you hold the script string, how can you insert it into the client page? In ASP.NET, you must register it with the page to be added to the markup at rendering time. The ClientScriptManager class provides methods to do that for regular pages; the ScriptManager class provides an analogous set of methods for AJAX pages.

 

Dino Esposito is a mentor with Solid Quality Mentors (http://www.solidq.com) and specializes mainly in ASP.NET and AJAX solutions. He s the author of Introducing Microsoft ASP.NET AJAX (Microsoft Press, May 2007) and wrote the two volumes of Programming Microsoft ASP.NET 2.0, also for Microsoft Press. Late-breaking news is available at http://weblogs.asp.net/despos.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish