ASP.NET Custom Controls -- Client Script Generation

Introduction

The new state management and postback features of ASP.NET are indeed very exciting. They provide developers with a whole new range of mechanisms for producing dynamic Web pages. The ability to write your own custom controls takes that ability to a whole new level, allowing you to write a control with custom functionality that can easily be reused in multiple pages by simply defining custom tags, similar to any other HTML element. The person implementing the layout for a page no longer needs to know all the details of how to write client-side code to get the dynamic behavior that has become so popular. However, there are some pitfalls that developers need to be aware of. ASP.NET promotes server-heavy designs. Network traffic can be dramatically increased as each client-side event can potentially cause a round trip to the server. Many of the effects that result from these frequent trips to the server can easily be accomlished with a few simple JavaScript functions. Calls to the server should be kept to a minimum, with as much being done on the client as possible. By using custom controls to generate client-side script, we can take advantage of Dynamic HTML on the client while still providing a measure of separation between the layout and the logic.

Client-Side Script Generation

One of the goals of generating script from a custom control is to allow a developer to create the control and specify its behavior, and then publish it for others to use without having to know how the code works. We want to encapsulate the implementation of the control and tightly couple the HTML rendering to the script that works with it to reduce the possible points of failure associated with more traditional methods of Web component reuse (such as Cut & Paste, and include files). The most straightforward approach to script generation is to write the script along with the control in the Render method of your control (see Listing 1).

This approach works, and does solve the initial problem of allowing a developer to write a custom control that someone else can use in their page to provide dynamic capabilities without having to post back to the server. However, it is not very elegant, and it does have some shortcomings. Most notably, we cannot include this control in a page multiple times; doing so would cause mutiple divisions to be created with the same ID. Even if we do uniquely name the <div> elements in this example, it is still inneficient because the JavaScript gets written out with every reference to this control. This can produce a lot of overhead, transmitting the same script down to the client for each instance of the control.

We need some way to have a control generate script, but generate it only once, even if multiple instances of the control are used on the same page. Fortunately for us, the developers at Microsoft thought of this and provided a way to register a script block to ensure we only write out a section of script once by using the Page.RegisterClientScriptBlock method. This method takes two parameters, an ID that identifies the script block so the Page class will know to ignore any other requests to register the same clock of code, and a string containing the script to be registered. The best place to register the script block is in the Init Event Handler for the control. To take advantage of this event, override the OnInit method of the Control class. With this in mind, the HelloWorld example could be rewritten as shown in Listing 3.

This approach is much better but there is still a problem. If the script that is being register is lengthy, or if there are a lot of calculations, data accesses, and so forth in generating our script, we will still take a performance hit when the page loads because the control creates this huge block of script that ends up being tossed out because it is already registered. Once a block of script is registered, we can test for it using the Page.IsClientScriptBlockRegistered method. To improve the performance of the HelloWorld control, we would include the call in out Init handler as shown in Listing 4.

Using client script generation from custom controls provides a clean encapsulated method of enabling dynamic behavior in Web pages while still shielding the page designer from having to know the details of how to produce the desired effect. Developers are now free to concentrate on how to get a control to do what you want it to do without being bogged down with were to put it on the page, or being pestered by the marketing guy to move a control around, add a new one, or take one away. By combining this approach with designer integration controls, dynamic behaviors can easily be customized and reused across multiple pages with little or no developer interaction and without the pitfalls of server-side includes or cut & paste code reuse.

Caching

Some of you might be inclined to ask: How do I cache this script so it doesn't get downloaded every time? After all, client-side script tends to be fairly static, not needing to be downloaded every time a Web page is loaded.

There are a couple of options for caching the output of your control. The ASP.NET approach would be to take advantage of output caching. There are a myriad of output caching options, but most of them place the responsibility of setting up that caching on the person doing the presentation by using directives and flags in the .aspx page. Also, caching the entire page may not be the desired effect. Some pages are extremely dynamic. In such cases, the ideal would be to just cache the control, or some portion of the control. ASP.NET does have some support for this, but that support is reserved primarily for user controls (.ascx files), which doesn't provide the reuse we are looking for.

For custom controls providing generated script, we may want to consider using an external script file. As we have already noted, most script does not change often, if at all and can readily be cached on the client. Instead of writing out the script directly from our custom control, we can instead place the script in an external script file and simply write out a <script..> tag with a src="." attribute that references our script file. This allows the control and the page to fluctuate as often as necessary without incurring the network traffic of always downloading the script to the client. The primary drawback to the approach is the deployment. There are now two files that need to be deployed to use the control in a page and the .js file must be reachable from the page that is using it. Relative paths don't always work because each page that uses the control may be at a different level. One deployment solution is to create a directory at the top level of your application (example: includes) and reference it in your control using Request.ApplicationPath + "/includes/<your script file here>". Another approach might be to provide custom properties on you control so the location of the external source file can be specified in the .aspx page. Listing 5 is an example of a calculator implemented using this approach.

Examining the Code

One item to note is that the reference to the stylesheet that defines the style for the calculator buttons is located in the OnInit method along with the script block registration. Registering blocks of client-side code is not limited to "script" alone. The stylesheet here is external, allowing the designer the ability to modify the look and feel of the buttons by modifying the .css file. Another approach to allowing the page designer to change the look and feel of the calculator would be to implement custom properties, or better yet, custom properties with sub-properties to group them together (example: Font-Style, Font-Size, and so forth). This approach seems somewhat limiting in that the designer then can only change the properties you have exposed. With stylesheets, the designer has all the options available to him/her that would be there if a standard HTML element was being used, options that would otherwise be unavailable because he/she does not have direct access to the HTML elements your custom control produces and would not be able to apply a class or style to them.

There is one block of script that is written out when the control is rendered instead of beign included in the .js file. This allows multiple instances of the calculator control to be used in the same page. The UniqueID property inherited from the Control class is used to differentiate the controls from each other. The UniqueID property is a unique identifier that identifies an instance of a control within a page.

The locations of the stylesheet and the external script file default to an /includes directory located at the virtual application root. However, there are two custom properties provided that allow the designer to override where those file are located.

By using the UniqueID for the control as the name for the submit button, we make sure that the LoadPostData method for our control only gets called when the 'Save' button for that control is clicked. If we had named the text box with the UniqueID for the control, we would end up saving the calculated number for all the controls on the page, regardless of how the submit to the server was done. This example is a little contrived and if you are really serious about reducing server load, you could alter the 'Save' button so that, instead of posting the form back to the server, it does a Web Services call.

Conclusion

Using custom controls to generate client-side script can have tremendous benefits. The custom control will look and behave similarly to any other control written with ASP.NET, making it easy to reuse and shielding the page designer from needing to know the details of how the code works. By using client-side scripting to create the dynamic behaviors, you can greatly increase the responsiveness of the individual pages and the overall performance of your web site by significantly decreasing the number of calls that are made to the server. Using external files for your script has both positives and negatives. The pros include taking advantage of browser caching and easy access for customizability. The cons include a more complex deployment both in the production environment as well as the design time environment.

Top White Papers and Webcasts

Live Event Date: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT
The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility.
Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …