Google

Thursday, November 29, 2007

How to refer a server side control inside client side script

Referencing a control inside a user control / master page on the client side is a little complicated because Asp.Net "fudges" the ID of all controls rendered as part of a user control, or a master page template. We'll look at the "why" of this a little later in the article, but let us look at the "how" of it with an example. Create a simple User Control which has a single button btnInput :

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="UsrCtrl.ascx.cs" Inherits="A_1_UsrCtrl" %>
<script language="javascript" type="text/javascript">
    function btnClicked(btnID)
    {
        var obj = document.getElementById("<%=btnInput.ClientID %>");
        alert('There is a button named : ' + obj.id + ' on this user control');
    }
</script>

<input type="button" runat="server" id="btnInput" value="Click Me" onclick="btnClicked()" />
Pay attention to the javascript function - we're getting the object using document.getElementById( ... ), and displaying an alert box with the ID of our button. Embed this on an Aspx page, and run it. When the button is clicked, you get the following error : So what happened ? To understand, view the source of your page (you can do this by right click on the page --> View Source). Look at our button control in the rendered HTML, it looks something like this :
<input name="UsrCtrl1$btnInput" type="button" id="UsrCtrl1_btnInput" value="Click Me" onclick="btnClicked()" />
What happened ? The ID of the User Control has been prepended to the front of the button's ID. You might be wondering, "why on earth would asp.net fudge the ID of controls". To understand why, imagine a situation where you have created five different user controls, each having a label named "btnInput". When you put all these five user controls on a single page, and if Asp.Net renders all the controls as is, we would have five buttons on the same webpage, having the ID "btnInput". This would not be acceptable, since each HTML element on a web page should have a unique ID. But in this case, when the page renders, there would be no way to distinguish which button is from which usercontrol, since all of them have the same ID. That is the reason for the following rule in Asp.Net : "If a control is embedded in a user control / master page, it's ID property is prepended with the parent user control's ID when the page is rendered". That is the reason that the user control's ID has been prepended to the button's ID in our above example. But this behavior creates problems in client side script. You would normally try to refer to a control as
document.getElementById([control id]
But in our case, you can't just use the control's ID. You have to use the actual "fudged" ID of the control. To account for this behavior in our client side javascript, we can use a handy asp.net control property called "ClientID". This property is always set to the actual ID of the control on the rendered page. Which means that if the control is not inside a user control, the ClientID would be same as the ID, but if the control is inside a user control, the ClientID will be [User Control ID]_[Control ID]. For e.g. in our case, ClientID will be set to "UsrCtrl1_btnInput". Here's how to use it to get the actual ID of a page webcontrol :
    function btnClicked(btnID)
    {
        var obj = document.getElementById("<%=btnInput.ClientID %>");
        alert('There is a button named : ' + obj.id + ' on this user control');
    }
Look at the highlighed code. The magic here is the <%= ... %> block. What this does is that when the page is rendered(as html), it executes the code between the
 <%= ... %> 
, and places the result of the embedded code in its place. Please note the fact that the code blocks are evaluated after the Render page method fires. So, if you are changing the server side variable in different methods, (say, page_load, preRender, and some event handler), only the most recent value will be reflected on the client side (in our case, the value set in the preRender method). The above highlighted line looks like the following when the page is rendered :
var obj = document.getElementById("UsrCtrl1_btnInput");
Once you have this, you can access the control on the client side just like any other normal control. Interestingly, the <%= %> blocks are not limited to only control properties. You can even call the page's public methods, and the returned values will be substituted in the output HTML. For e.g., let's say we have a C# method codeBehindMethod() that always returns "TEST". The following line is gonna show an alert box with the message "TEST".
alert(<%= codeBehindMethod() %>);

Until now, we saw how to access a server side variables in client side script. However, it is also possible to run your C# code on the client side. This can be put to use to achieve some pretty sleek functionality. In my next post, we'll see how to execute C# code on the client side to achieve convenient results.

1 comment:

Peter said...

This doesn't seem to work with custom controls that are registered with a TagPrefix.

When a tagprefix is defined for a control, this gets prepended (if that is a word) on the clientid. the problem is that the ClientID-property doesn't seem to know this.

Do you know how to solve this?