Tuesday, March 10, 2009

Calling an ASP.NET server side function from Javascript.

I know this blog discusses a very simple problem but this simple problem is being confused by many and there are lots of suggestion floating in the net for the solution, which needless to say is an exaggerated solution to a simple problem. This blog is purely meant to clear those myths, else I wouldn’t have blogged on such a simple issue in so length.

Many of us would have come across a scenario where we needed to call a server side asp.net function and retrieve a value, from within javascript/HTML. I have come across this scenario many a times. But, recently, while going through some forums I found many people giving suggestions to make use of ScriptManager, ASP.NET Ajax controls and other stuffs. ScripManager, ASP.NET Ajax controls etc are purely meant for enabling AJAX/Partial page loading concept. But the real problem here is to just call a server side function or access a server variable and use it in javascript or html.

I would say, without using all these high funda stuffs we can solve this problem pretty easily using inline server codes. Though I am not a very big fan of this type of coding but there is no escaping from it, I had to use inline server code in many places to embed some server side variable’ value or the return value of some function in javascript or html tags. The syntax used for doing so is <% Server side code goes here %>. One can write any server side code/logic either in C# or VB in between “<% %>”, but in this blog I will concentrate on retrieving output of a server side function as there are lots of myths associated with such a simple thing.

I am a big fan of localization and so I use resource files (wherever possible) or XML file to implement localization. In many situations while writing javascript I had to show the localized strings retrieved from the server. I have used the simple code shown below to retrieve data from the server.

<%@ Page Language="C#" MasterPageFile="~/MasterPages.Master" AutoEventWireup="true" CodeBehind="MyProjectHome.aspx.cs" Inherits="Home_TDI" Title="Home Page" %>
<%@ Import Namespace="Utilities"%>
<%@ Import Namespace="StaticContents" %>
<asp:Content ID="cnHomeTDI" ContentPlaceHolderID="mainContent" runat="server">

<script language="javascript" type="text/javascript">
var ajaxRequest;
// Script block to ping the server and get an AJAX request.
function sendAjaxRequest()
{
ajaxRequest = new XMLHttpRequest();
ajaxRequest.onreadystatechange = HandleResponse;
ajaxRequest.open("GET", "home.aspx?function=GetSomeCount", true);
ajaxRequest.send(null);
}

//Function which handles the AJAX response.!!! Sandeep
function HandleResponse()
{
if (ajaxRequest != undefined && ajaxRequest != null)
{
if (ajaxRequest.readyState == 1)
{
showText('<% =ResourceManagerUtility.GetString(Constants.PLEASE_WAIT) %>')
}
else if (ajaxRequest.readyState == 2)
{
showText('<% =ResourceManagerUtility.GetString(Constants.REQUEST_SENT) %>');
}
else if (ajaxRequest.readyState == 3)
{
showText('<% =ResourceManagerUtility.GetString(Constants.RECEIVING_REQUEST) %>');
}
else if (ajaxRequest.readyState == 4 && ajaxRequest.status == 200)
{
showText(ajaxRequest.responseText);
}
else if ((ajaxRequest.readyState == 4) && (ajaxRequest.status != 200))
{
alert('<% =ResourceManagerUtility.GetString(Constants.
SCREWED_UP_REQUEST) %>');
}
}
}

function showText(textToBeDisplayed)
{
var lblCntl = document.getElementById('<% = noRecordsFound.ClientID %>');
if (lblCntl != undefined && lblCntl != null)
{
lblCntl.innerHTML = textToBeDisplayed;
}
}
</script>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td></td>
</tr>
<tr>
<td>
<% =ResourceManagerUtility.GetString(Constants.WAITING) %>
</td>
</tr>
<tr>
<td>
<label id="noRecordsFound" runat="server" visible="false">
<% =ResourceManagerUtility.GetString(Constants.NO_RECORDS_FOUND) %>
</label>
</td>
</tr>
</table>
</asp:Content>

In the above code one can see that I am making use of the below line.

“<% =ResourceManagerUtility.GetString(Constants.[CONSTANT_NAME]) %>”

What the above code does is it invokes the static “GetString” method defined in “ResourceManagerUtility” class and retrieves the corresponding string attached to the constant without making use of the so called ScriptManager or ASP.NET AJAX or any other stuffs. The code for ResourceManagerUtility and Constants classes is shown below.

namespace TDI_Tool_Prototype.Utilities
{
public class ResourceManagerUtility
{
/// <summary>
/// Returns the localised strings from the default localised resource file.
/// </summary>
/// <param name="theKey">The key based on which the localised text has to be retrieved.</param>
/// <returns>Returns a string from the resource file.</returns>
public static string GetString(string thekey)
{
//Creating an instance of ResourceManager.
System.Resources.ResourceManager resourceManager = new
System.Resources.ResourceManager ("English", System.Reflection.Assembly.GetExecutingAssembly());
return resourceManager.GetString(thekey);
}
}
}

“GetString” method creates an instance of “ResourceManager” class and returns the value based on the key passed in the “theKey” argument of the function.

The “Constants” class structure is shown below.

public class Constants
{
public const string PENDING_INVESTMENT_REVIEW = "PENDING_REQUEST";
public const string NO_PENDING_INVESTMENT_REVIEW = "WAITING";
public const string SCREWED_UP_PENDING_REVIEW = "SCREWED_UP_REQUEST";
public const string PLEASE_WAIT = "PLEASE_WAIT";
public const string REQUEST_SENT_TO_SERVER = "REQUEST_SENT";
public const string RECEIVING_REQUEST_FROM_SERVER = "RECEIVING_REQUEST";
public const string NO_RECORDS_FOUND = "NO_RECORDS_FOUND";
public const string OPEN_CONNECTION = "OPEN_CONNECTION";
}

The above class holds only the constants. These constants are passed to the “GetString” method and based on these constants the localized strings are retrieved.

In many blogs I have seen people saying one can access only methods in a page’ code behind file, I would like to say it’s not true. One can very well access any method inside any class, namespace or dll by adding a reference to the namespace. In the above code I am accessing “ResourceManagerUtility” class from another namespace called “Utilities” by importing it in the aspx page using the below code.

<%@ Import Namespace="Utilities"%>

The above code imports the Utilities namespace. The code is similar to the below C# code.

using Utilities;

Once you have imported the namespace using the @Import attribute you can use a syntax something like this.

<% =ClassName.StaticMethod([Arguments]) %>

So in the above examples I have used the above syntax in javascript where I am retrieving a text from the resource file and passing it to a javascript function called “showText”. Also I have made use of the above syntax in javascript’ alert message as well. The code snippets are pasted below.

// Passing the localised string retrieved from the server to a javascript function.
showText('<% =ResourceManagerUtility.GetString(Constants.PLEASE_WAIT) %>')

// Passing the localised string retrieved from the server to a javascript alert box.
alert('<% =ResourceManagerUtility.GetString(Constants.
SCREWED_UP_REQUEST) %>');

Also the same syntax is used in between html tags and for assigning the text value of a label control. The code is pasted below.

<!--Writing the localised text in a table' column-->
<tr>
<td class="SubSection">
<% =ResourceManagerUtility.GetString(Constants.OPEN_CONNECTION) %>
</td>
</tr>
<!--Assigning the localised string to the text property of a label control-->
<label id="noRecordsFound" runat="server" visible="false" style="font-size:large;"><% =ResourceManagerUtility.GetString(Constants.NO_RECORDS_FOUND) %></label>

One could see from the above code snippet that how easy is to access server functions in javascript, html tags and also in server control tags.

Possible mistake while using the above syntax can be as follows.

  • Not making use of the ‘=” symbol before the server code. E.g. is shown below.
  • <% ResourceManagerUtility.GetString(Constants.OPEN_CONNECTION) %>

    The above statement can throw the below pasted error if used in javascript or in html or with server controls.

    Compilation Error

    Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
    Compiler Error Message: CS1002: ; expected

  • Next thing what any sane person would do on seeing the error is placing a “;” (semi colon) at the end of the expression as shown below.
  • <% ResourceManagerUtility.GetString(Constants.OPEN_CONNECTION); %>

    The above code is perfectly correct if it is placed in between html tags or used along with server control tags. The modified code without the “=” sign is shown below.

    <!--Writing the localised text in a table' column-->
    <tr>
    <td class="SubSection">
    <% ResourceManagerUtility.GetString(Constants.OPEN_CONNECTION); %>
    </td>
    </tr>
    <!--Assigning the localised string to the text property of a label control-->
    <label id="noRecordsFound" runat="server" visible="false" style="font-size:large<% ResourceManagerUtility.GetString(Constants.NO_RECORDS_FOUND); %></label>

    But the same code without the “=” will not work if used in javascript. The code using the “alert” statement is modified (without “=”) and pasted below.

    alert('<% ResourceManagerUtility.GetString(Constants.
    SCREWED_UP_REQUEST); %>');

    The above code will invoke the method and get the value but the alert message will be a blank one. The reason is the absence of the “=” sign. The “=” sign means an assignment, as the “=” sign is missing; the value is not getting assigned. So if you are trying to get some value from the server side in javascript then one has make use of the code with ‘=” sign and without the semicolon.

    alert('<% =ResourceManagerUtility.GetString(Constants.
    SCREWED_UP_PENDING_REVIEW) %>')

  • “=” sign along with “;” in javascript or html or along with sever controls. If someone is using “=” along with “;’ in the expression then the below error will be thrown.
  • Compilation Error

    Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
    Compiler Error Message: CS1026: ) expected

    The error prone code is shown below.

    // Error prone javascript code having both = and ;.
    alert('=<% ResourceManagerUtility.GetString(Constants.
    SCREWED_UP_PENDING_REVIEW); %>');

    <!--Error prone HTML code with = and ; used together-->
    <tr>
    <td class="SubSection">
    <% =ResourceManagerUtility.GetString(Constants.
    OPEN_CONNECTION); %>
    </td>
    </tr>

    To avoid the error, either use “=” without “;” with inline code in javascript and “;” without “=” with inline code in html. To be on the safer side one can safely use only “=” sign along with inline code in both javascript and html tags as shown below.

    // Perfect and safest way to use the same technique
    alert('<% =ResourceManagerUtility.GetString(Constants.
    SCREWED_UP_PENDING_REVIEW) %>');

    <!--The same way as it is used in the javascript-->
    <tr>
    <td class="SubSection">
    <% =ResourceManagerUtility.GetString(Constants.
    OPEN_CONNECTION) %>
    </td>
    </tr>

    I would recommend the above approach that works with javascript, html tags as well as with server control tags. Just for the sake of knowledge you can keep yourself aware about the other ways as well, but while implementing please follow the common methodology which will help you to avoid hell lot of problems.

    Also in some blog I have read someone saying that only static methods can be accessed, this is not at all true. One can very well access normal methods of a class by creating an instance of the class and then accessing them in inline code as shown below.

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Dummy.aspx.cs" Inherits="MyProject.Dummy" %>
    <%@ Import Namespace="Utilities" %>
    <%@ Import Namespace="StaticContents" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
    <title>Showing normal method access in inline code.</title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
    <% =dummyString%>
    <table>
    <% ResourceManagerUtility rmu = new ResourceManagerUtility();%>
    <tr>
    <td><!--Accessing an instance method and assigning the output in a table' column by making use of inline code.-->
    <% =rmu.GetString(Constants.NO_RECORDS_FOUND) %>
    </td>
    </tr>
    </table>
    </div>
    </form>
    </body>
    </html>

    One can notice a line something like this.

    <% =dummyString%>

    What I am doing in the above line of code is accessing a server side variable and displaying the value. Below is the code behind file where the variable is declared.

    public partial class DisplayFile : System.Web.UI.Page
    {
    protected string dummyString = "Dummy String";
    // Other code for the page goes here.
    }

    One point to note here is that one can only access protected or public variables. If one tries to access private variables in inline code the below pasted error will crop up.

    Compilation Error

    Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
    Compiler Error Message: CS0103: The name 'dummyString' does not exist in the current context

    The same applies to methods also. One cannot access private code behind methods in inline code. The only restriction which applies in the case of inline code is one cannot access private members. Static, protected and public members can very well be accessed.

    The only reason which I find why many are saying that only static methods can be used in inline codes is that one need not create a member variable to access the return value of a function. Other than that I don’t see any logic in saying this. One can very well access any protected, public and static members (variables, methods) of a code behind file or a class.

    Hope the above e.g. clears some of the myths associated with accessing server side methods, variables and classes.

    9 comments:

    1. Very good post. Thanks.

      ReplyDelete
    2. Thanks for the positive comments.

      ReplyDelete
    3. Very helpful post.

      ReplyDelete
    4. Thats great. How can we use a constant from constants.vb in the user control? I am trying it just like this and as soon as I put ResourceManagerUtility.GetString alert to this (inside server side symbols)(Constants.DESCRIPTION_LENGTH_LONG),the user control itself becomes unrecognizable and the application shows many build errors.

      ReplyDelete
    5. Hi,
      It will be great if you could paste some code so that I can better understand your problem. Or you can send me the code to sndppr@gmail.com. From what you have explained I am not sure what is the problem.

      ReplyDelete
    6. Hi Sandeep,
      This is a great post... But simplicity always wins... You have used too many code to explain the concept... when I read this post its bit hard to follow the code as it is too verbose...

      ReplyDelete
    7. Thanks for the suggestion. Will incorporate the same in future posts.

      ReplyDelete
    8. Such a great done by you in this article. Your article information is very helpful and useful for us. Keep it up. Now it's time to avail Baby Liquid Soap for more information.

      ReplyDelete

    Please provide your valuable comments.