Click here to Skip to main content
Click here to Skip to main content
Technical Blog

JavaScript and User Controls 101

, 3 Jun 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Learn how to best embed/associate JavaScript with ASP.NET user controls

I've been doing quite a bit with user controls and JavaScript. There is an art to using the JavaScript correctly, wiring it in and having it work consistently. I thought I'd post a brief article on the "101" around this based on the things I've found. Hopefully this can be a quick-and-dirty reference guide for those of you working to extend your user controls with JavaScript functionality.

Understand Multiplicity

First, understand if your user control can potentially be rendered multiple times on a page. For the most part, I assume this will be the case. However, there are some circumstances, such as a large grid control, where you may absolutely know there is only going to be a single instance. The reason you want to understand this is because you want to try to have exactly one copy of the JavaScript code emitted to the browser, so it will need to be "aware" of the instance it is working with.

JQuery

I am a major fan of JQuery because of the fact it feels "natural" with the combination of XPath selector and CSS-style selections, chaining, and the browser compatibility is handled for me. There are correlating functions in other libraries, but this is what I'll be using for my examples.

Part 1: The ASCX File

I try to keep as clean a separation of code from the ASCX file as possible. The exceptions are elements that I must derive on the server side. These typically include references for callbacks, URLs (I like to strongly type my URL references), and wiring "init" events.

We'll use a hypothetical "contact" control. Here is what I have at the bottom of my ASCX file, and this assumes I am using the AJAX framework and therefore have somehow registered a ScriptManager (this is outside the scope of this document — see the Script Manager control overview:

window.Sys.Application.add_init(contactInit);
contactCtrlClientID = '<%=ClientID%>';

Basically, I've registered with the AJAX framework and told it to call contactInit once everything is loaded. I've also created a reference to the client id. If I'm going to have multiples, I'd push them to an array or use some other method to keep track (windows provides handles and references in AJAX as well) of them ... this is just a quick and easy way to know how it is registered to the page if it is inside of a master page, etc.

The JavaScript File

There are many ways you can include the related JavaScript. Some people like to make it a straight include reference so they can easily update the JavaScript on disk and test changes, etc. We produce commercial software so the code is embedded (I like to do initial testing with an external resource, then embed the JavaScript). My favorite convention is to name the JavaScript the same as the control and have it side-by-side with the control in the designer (i.e. if my control is ContactCtrl.ascx, then my JavaScript is ContactCtrl.js and at the same level as the control).

Embedding your JavaScript

This is an easy three-step process.

  1. Right click on the properties of your JavaScript file and indicate you wish to make it an Embedded Resource. If you don't want to ship the source, make sure you have "do not copy" selected for the destination.
  2. Your web project should have a Properties folder with AssemblyInfo.cs. At the bottom of this file, you need to add a tag to identify the embedded resource. You will use the namespace, including the folders, and then the filename itself. For example, let's say the namespace for the project is Company.Project and the control is in a UserControls/Contact folder. Your resource will look like this:
    [assembly: WebResource
        ("Company.Project.UserControls.Contact.ContactCtrl.js", "text/javascript")] 
  3. Finally, register a reference to it. When you register an embedded resource, you must specify a type. This ties the code to the control and prevents it from being emitted multiple times if the control appears multiple times. It is important to use typeof instead of GetType() or it may not behave as expected. Our example, which can go in onPreRender or onLoad:
    Page.ClientScript.RegisterClientScriptResource(typeof(ContactCtrl), 
    	("Company.Project.UserControls.Contact.ContactCtrl.js"); 

Initialization

When you register an initialize event, you can then hook in to "prep" the control. For example, maybe my contact control has some dynamic sections that are conditional based on a contact type. I can do something like this:

function contactInit() {

   $(document).ready({
      $("#divAdmin").hide();
      $("#selType").change(contactTypeChange); 
   });

}

function contactTypeChange() {

...
   $("#divAdmin").show();

}

Essentially, this will hide the admin section, then wire in a change event to the dropdown and call the type change method which can conditionally show it, etc.

Events

Good development practice dictates I design my control as self-contained as possible. For example, if I am making a search criteria control, I can't just assume I'll have a "search results grid" and when the user clicks "submit" send some client information over to the control. The proper way to have user controls talk on the client side is the same way on the server: through events. There are two ways I like to wire in my events. If I'm doing a light weight control, I'll tab into the global event pool and have a unique event name. If I'm developing a more extensive control, I'll wire in an AJAX behavior and expose events that way. To learn how to create a full-blown AJAX client control (something that can expose all of your server side events in a similar clientside model), read Creating Custom ASP.NET AJAX Client Controls.

Let's assume I want to expose an event that is raised whenever the user changes the contact type. This way I can have other controls that might dynamically respond to the change on the client side, avoiding a server round trip. I don't have a true AJAX client control that corresponds to my contact control, so what can I do? An easy method is to plug into the Application events - think of these as "global" events. In my contact, I just need to know if someone is listening to my event. I'll give it the name "contactTypeChange" and send along the new type as well as my client id in case I have multiple instances.

function contactTypeChange(clientID,contactType) {

   var contactHandler = 
	window.Sys.Application.get_events().getHandler("contactTypeChanged");
   if (contactHandler) {
      contactHandler(clientID,contactType);
   }
}

Now, in my control that is listening to the event, I simply register to do something with it. Let's call this my "Contact Security" panel and it will show special admin security rights if the contact is an admin. First, in my init function, I'll register for the event. Second, I'll provide a function to handle the event.

function contactSecurityInit() {

   window.Sys.Application.get_events().addHandler
	("contactTypeChanged", contactTypeChangeHandler); 

}

function contactTypeChangeHandler(clientID, contactType) {

   if (contactType == "admin") {
      $("#divSecurityAdminInfo").show(); 
   }

}

Callbacks

Finally, a caveat on callbacks. I use these a lot, because of the fact that update panels tend to add a lot of overhead. If your application requires JavaScript, you can manage a lot of the state in the browser page and use more tightly defined callbacks to perform dynamic updates. One "gotcha" with callbacks is that you must ask for the callback function in order for the control to listen for it. The three steps I find are easiest to manage callbacks:

  1. Implement ICallbackHandler on your UserControl. This will generate two methods: one that takes an event argument, and one that asks for a return result. That's it! The event argument is what you control on the client side and pass down. In your control, you'll process some result and then can send back something to the client. It can be as simple as a little message to display, or as complex as a dynamic snippet of JavaScript to eval() on the client.
  2. Ask for the callback reference. This is important! Even if you will explicitly callback from your code, you need to "ask" for the reference in order for the control to listen. It's as simple as:
    Page.ClientScript.GetCallbackEventReference
    	(this, string.Empty, string.Empty, null);
  3. Wire in the call!

For example, let's say we want to display some text in a div based on the contact type. We want the server to retrieve the text and perform globalization functions against it before sending it back from the browser. This snippet of code will do the trick:

WebForm_DoCallback(callbackRef, contactType, function(args, ctx) { 
   $("#divHelp").html(args);    }, null, null, false);

Callbackref is the reference to the control. You can embed the GetCallbackEventReference above in your ASCX page and render the page to see how the control is referenced. It's essentially the path to the control separated by dollar signs (if you have a masterCtrl then a pageCtrl then the contactCtrl, it will look like masterCtrl$pageCtrl$contactCtrl). The next is the argument that gets passed to the server, following by the function to process when the result is returned. Here, we do an inline function and find the div and fill it with what we got back from the server. (You can read more about script callbacks).

Conclusion

Obviously, there is a lot you can do with scripts and user controls and this post only scratched the surface. Hopefully this gave you a good idea of some good practices for associating JavaScript to a user control and ways you can use JavaScript to extend the functionality, allow for greater interoperability between controls, and streamline performance through the use of callbacks.

Jeremy Likness

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jeremy Likness
Architect Wintellect
United States United States
Jeremy Likness is a principal consultant at Wintellect. Jeremy, an experienced entrepreneur and technology executive, has successfully helped ship commercial enterprise software for 20 years. He specializes in catalyzing growth, developing ideas and creating value through delivering software in technical enterprises. His roles as business owner, technology executive and hands-on developer provided unique opportunities to directly impact the bottom line of multiple businesses by helping them grow and increase their organizational capacity while improving operational efficiency. He has worked with several initially small companies like Manhattan Associates and AirWatch before they grew large and experienced their transition from good to great while helping direct vision and strategy to embrace changing technology and markets. Jeremy is capable of quickly adapting to new paradigms and helps technology teams endure change by providing strong leadership, working with team members “in the trenches” and mentoring them in the soft skills that are key for engineers to bridge the gap between business and technology.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 1 Pingrouphector garduno18-Nov-10 9:54 
QuestionServerControl considered? PinmemberKinStephen1-Jun-09 20:55 
First off, nice article!
 
Have you considered a variation showing how to do through a server control instead of an ascx?
AnswerRe: ServerControl considered? PinmemberJeremy Likness2-Jun-09 1:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411023.1 | Last Updated 3 Jun 2009
Article Copyright 2009 by Jeremy Likness
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid