Monday, December 3, 2007

Masterpage – document.getElementById Issue

Masterpage is one of the good features of the ASP.NET 2.0. But if you are a beginner in .NET 2.0 and Masterpage, you would stuck up to get the element inside a Masterpage from a JavaScript code.


The issue was, whenever a control is inside a Masterpage the client id of the control would get append with the content placeholder id. So for an element of id “txtTest” the client id would be something like “ctl00_ContentPlaceHolder1_ txtTest”.


So when you use the document.getElementById(‘txtTest’), you will not get the access of the txtTest textbox in JavaScript. Indeed you need to access it by calling document.getElementById(‘ctl00_ContentPlaceHolder1_ txtTest’).


To avoid this we can access the control by using the document.getElementById('<%=txtTest.ClientID%>'). This will give you the access to txtTest text box. Now this will work fine until and unless the script is in line with the aspx page ie., if the script is included as a part of the aspx page. But the same won’t work if you have this script in a separate .js file, and add it to the aspx page.


So in this scenario to get the access to the control, we need to hard code the control’s id. But hard coding is not an ideal way of coding. To avoid this situation what we can do is to, maintain the mapping between the client id and the server id. This can be achieved as shown below.


First we need to declare two arrays and the first array will have the server ids of the required controls, and the second array will have the client ids of those server controls in the same order. Register these two arrays to the client side. Now create a JavaScript function, which will accept the server id and will compare the server id with the available server ids in the array and will give its position in the array. Then the same function would return you the matching client id in the same location of the client array. So we were able to get the mapping client id for the given server id.


The code below shows the declaration of the array and the declaration of the JavaScript function.


public void RenderJSArrayWithCliendIds(params Control[] wc)

{
if (wc.Length > 0)
{
StringBuilder arrClientIDValue = new StringBuilder();
StringBuilder arrServerIDValue = new StringBuilder();
// Get a ClientScriptManager reference from the Page class.
ClientScriptManager cs = Page.ClientScript;
for (int i = 0; i < wc.Length; i++)
{
arrClientIDValue.Append("\"" + wc[i].ClientID + "\",");
arrServerIDValue.Append("\"" + wc[i].ID + "\",");
}

cs.RegisterArrayDeclaration("MyClientID", arrClientIDValue.ToString().Remove (arrClientIDValue.ToString().Length - 1, 1));
cs.RegisterArrayDeclaration("MyServerID", arrServerIDValue.ToString().Remove(arrServerIDValue.ToString().Length - 1, 1));
cs.RegisterStartupScript(this.Page.GetType(), "key", "\nfunction GetClientId(serverId)\n{\nfor(i=0; i<MyServerID.length; i++)\n{\nif ( MyServerID[i] == serverId )\n{\nreturn MyClientID[i];\nbreak;\n}\n}\n} ", true);
}
}

We can make these codes as part of a common class for all the UI pages, so that in each page if need to access any one control in the JavaScript we can simply call the below function with the parameter as the controls to be accessed.


// Here give the coma seperated controls for which the client ids should be rendered
RenderJSArrayWithCliendIds(txtTest,lblTest);

This is one of the simplest ways to have a mapping between the client and the server id. By this way no need to worry about accessing a control inside a MasterPage from an external javascript files.

After this codes we can access the elements which are given in the RenderJSArrayWithCliendIds function in the javascript as shown below


// Get the text box
var txtTest = document.getElementById(GetClientId("txtTest "));


This will slove the issues arrised due to the change in the client id of the control which is placed inside a MasterPage.


Updates: I am not much experienced in VB, but still tried to translate the C# code to VB. Just check the below code.


Public Sub RenderJSArrayWithCliendIds(ByVal wc As Control())
If (wc.Length > 0) Then
Dim arrClientIDValue As StringBuilder = New StringBuilder()
Dim arrServerIDValue As StringBuilder = New StringBuilder()
Dim cs As ClientScriptManager = Page.ClientScript
Dim frm As HtmlForm = Page.Form
Dim i As Integer
For i = 0 To wc.Length
arrClientIDValue.Append("\"" + wc[i].ClientID + " \ ",")
arrServerIDValue.Append("\"" + wc[i].ID + " \ ",")
i = i + 1
Next
cs.RegisterArrayDeclaration("MyClientID", arrClientIDValue.ToString().Remove(arrClientIDValue.ToString().Length - 1, 1))
cs.RegisterArrayDeclaration("MyServerID", arrServerIDValue.ToString().Remove(arrServerIDValue.ToString().Length - 1, 1))
cs.RegisterStartupScript(Page.GetType(), "key", "\nfunction GetClientId(serverId)\nBegin\nfori=0; i<MyServerID.legth; i++)\nBegin\nif ( MyServerID[i] == serverId )\nBegin\nreturn MyClientI[i];\nbeak;\nEnd\nEnd\nEnd\n \nfunction GetObject(serverId)\nBegin\nfori=0; i<MyServerID.legth; i++)\nBegin\nif ( MyServerID[i] == serverId )\nBegin\nreturn document.all[MyClientIDi]];\nbeak;\nEnd\nEnd\nEnd\n", True)
End If
End Sub

Friday, October 19, 2007

parseInt - Javascript issue

What would be the result of the following javascript

var value = "08";
value = parseInt(value);
alert(value);

When we just go through the above script, we would expect the result as "8" should be alerted. But to all our surprise the result would be "0".

Let us analyze how this happened.

when ever the browser tries parseInt("08"), zero in front tells the browser that this is an octal (base 8) number and hence will be returned as "0".

This is the default behavior of the parseInt function, to read more about the function
click here.

So what we need to do in this case?

If we just check the intellisence result for the parseInt, it will be like


So it accepts another one argument called radix, and the definition of radix is as follows

"The radix parameter is used to specify which numeral system to be used, for example, a radix of 16 (hexadecimal) indicates that the number in the string should be parsed from a hexadecimal number to a decimal number."

And if the radix parameter is not supplied, browser assumes the following:
  • If the string begins with "0x", the radix is 16 (hexadecimal)
  • If the string begins with "0", the radix is 8 (octal). This feature is deprecated
  • If the string begins with any other value, the radix is 10 (decimal)

So in our case, browser assumed the radix as 8 and returned a value of "0". To solve this issue whenever we are calling the parseInt we should explicitly specify the radix value.

Thursday, October 18, 2007

XML PARSE ERROR: Missing end-tag - Error in Export to Excel

Last week i was debugging a strange problem which came in the export to excel option in one of our web page. We have a export method which, will accept the dataset and will loop through it and builds the XML schema for the excel.

When we were using this excel method to export some data, it went fine and the response rendered the Open or Save dialog box. But when we try to open the excel file it gave a error stating the following





Problem came up in following areas during load:

Table

This file cannot be opened because of errors. Errors are listed in: C:\Documents and Settings\UserId\Local Settings\Temporary Internet Files\Content.MSO\90797611.log




And when we open the specified 90797611.log file the message it had was,






XML PARSE ERROR: Missing end-tag
Error occurs at or below this element stack:
<ss:Workbook>
<ss:Worksheet>
<ss:Table>
<ss:Row>
<ss:Cell>
<ss:Data>
<ss:Font>
<ss:B>
<ss:I>








By looking in to this i was not able to get any thing, after doing some R&D I came to a conclusion that there were some problem with regarding "<ss:Font> <ss:B> <ss:I>".






Finally i decided to have a look at the dataset which is supplied to the export method, and to my surprise there was a field with the following data in it






<Font colour="green"><B><I>7358</B></I></Font>






As we are building a XML, the <Font colour="green"> got treated as a XML tag and it was expecting a corresponding end tag. This created the whole problem.



After removing <Font colour="green"><B><I> from the data, every thing worked fine.