Client-Side Script Features





Client-Side Script Features

Although ASP.NET is a server-side programming environment, there are times when client-side script is required. This is especially true when dealing with smart browsers, such as Internet Explorer, where programming client-side can enhance the user experience. To improve this interaction ASP.NET 2.0 introduces several new features, including the ability to set the focus of a control, the concept of a default button, and some advanced client-to-server processing.

Form Focus

To set the client-side focus, there are two new methods and an attribute on the form. Each control has a Focus method:

btn1.SetFocus()

The Page has a SetFocus method:

Page.SetFocus(btn1)

Finally, the form has an additional attribute (DefaultButton) to set the default button:

<form runat="server" DefaultButton="btn2">

These can be used as shown in Listing 9.5.

Using the Focus and SetFocus Methods
<script runat="server">

  Sub btn1_click(sender As Object, E As EventArgs)
    btn3.Focus()
  End Sub

  Sub btn2_click(sender As Object, E As EventArgs)
    ...
  End Sub

  Sub btn3_click(sender As Object, E As EventArgs)
    Page.SetFocus(btn1)
  End Sub

</script>
<form runat="server" DefaultButton="btn2">
  <asp:Button id="btn1" Text="Button 1"
      onClick="btn1_click" runat="server" />
  <asp:Button id="btn2" Text="Button 2"
      onClick="btn2_click" runat="server" />
  <asp:Button id="btn3" Text="Button 3"
      onClick="btn3_click" runat="server" />
</form>

The default focus is applied only when no other focus methods are used.

Client Click Events

For server controls, the OnClick event refers to a server event. To hook a client-side event procedure into the click of a server button requires adding an attribute. To make this easier, ASP.NET 2.0 has the OnClientClick method:

<asp:Button runat="server" OnClientClick="ClientScript" />

The onClientClick event is supported by the Button, ImageButton, and LinkButton controls.

Registering Script Blocks

Programmatically adding client-side script to a page is achieved with RegisterClientScriptBlock and RegisterStartupScript, which both take a key and the script. This causes problems because it's not possible to use the same key for both a start-up script and another script block. To preserve compatibility, these methods still work, but a new ClientScriptManager, accessible from the ClientScript property of the page, provides enhanced features. For example:

Sub Page_Load(Sender As Object, E As EventArgs)

  Dim Script As String = "alert('Morning everyone');"

  Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
                                 "FormValidation", Script)

End Sub

An additional parameter has also been added to allow the insertion of the <script> tags. For example:

Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
                               "FormValidation", Script, True)
Script Includes

In version 1.x of ASP.NET, to include a reference to an included script you had to do the following:

Sub Page_Load(Sender As Object, E As EventArgs)

  Dim URL As String = Page.ResolveUrl("~/Scripts/MyScript.js")
  Dim Script As String = "<script language='javascript'" & _
                         " src='" & URL & "'></script>"

  Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
                                 "FormValidation", Script)

End Sub

With version 2.0 there is now a RegisterClientScriptInclude method to make this easier. For example:

Sub Page_Load(Sender As Object, E As EventArgs)

  Dim URL As String = "Scripts/MyScript.js"

  Page.ClientScript.RegisterClientScriptInclude(Me.GetType(), _
                                 "FormValidation", URL)

End Sub

This will generate the following:

<script language="javascript" src="Scripts/MyScript.js"></script>

Client Callbacks

Client callbacks allow client-side script to call server-side events asynchronously, without causing a postback. This opens up a whole avenue for creating responsive interfaces, where data can be fetched or commands can run in the background.

ASP.NET 2.0 introduces the CallBackManager, which acts as an intermediary between client code and server code. It is responsible for accepting requests from the client, firing the appropriate server event, and then passing the results back to the client, as shown in Figure.

5. Client callback architecture

graphics/09fig05.gif

Several things are required for this process to work.

  • The CallBackManager code must know the name of the client-side method that instantiates the callback. This is accomplished by using the GetCallbackEventReference method of the Page, which returns a string containing the client script required to initiate the callback.

  • A client-side method must be created to handle the data returned by the CallBackManager.

  • The browser must support XmlHTTP because it is this that provides the client functionality to send requests to the server. To check for this, the BrowserCaps object has two new capabilities—SupportsXmlHTTP and SupportsCallBacks. In version 2.0 these will reflect the same capabilities, but having them separate allows for the callback implementation to be changed so that it is independent of XmlHTTP.

We'll look at the individual implementation details first, then look at a simple example.

Implementing the Callback Event Handler

If a page or control wishes to receive callback events, it must implement the ICallbackEventHandler interface. For a control developer this is simply a matter of adding the Implements keyword to the class definition:

Public Class MyControl
  Inherits WebControl
  Implements ICallbackEventHandler

End Class

For a Web page the Implements directive is used:

<%@ Page Language="VB" %>
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>

Implementing the interface is simple because it has only one method, which is the method that will be called from the client. Its signature is:

Function RaiseCallbackEvent(eventArgument As String) As String

The single argument will contain data from the client, and the return value is returned back to the client. For example:

Function MyCallbackEvent(eventArgument As String) As String _
    Implements ICallbackEventHandler.RaiseCallbackEvent

  Return "The server saw " & eventArgument

End Function
Client-Side Script

On the client, there needs to be a minimum of two scripts—one to call the server, and one to act as the callback from the server. The latter of these must match the following signature:

<script>

  function MyCallback(result, context) { }

</script>

The return value from the server RaiseCallbackEvent will be passed into the above function in the result parameter, and context is passed from the context parameter of GetCallbackEventReference, which is described in the next subsection.

There can also be a third script, of the same signature as the MyCallback script, which is run if an error occurs during the callback procedure.

Generating the Client Callback Code

For the client script to call back to the server, the client needs to know the control implementing the ICallbackEventHandler interface. For this the GetCallbackEventReference method is used (in much the same way that GetPostbackEventReference is used for controls responding to postbacks). The GetCallbackEventReference method is overloaded, having the signatures shown in Listing 9.6.

GetCallbackEventReference Syntax
Public Function GetCallbackEventReference(
                control As Control,
                argument As String,
                clientCallback As String,
                context As String) As String

Public Function GetCallbackEventReference(
                control As Control,
                argument As String,
                clientCallback As String,
                context As String,
                clientErrorCallback As String) As String

Public Function GetCallbackEventReference(
                target As String,
                argument As String,
                clientCallback As String,
                context As String,
                clientErrorCallback As String) As String

Parameters for GetCallbackEventReference

Parameter

Description

control

The control that implements RaiseCallbackEvent

argument

A value to be sent to the RaiseCallbackEvent via the eventArgument parameter.

clientCallback

The name of the client-side event handler that will receive the results of the server event if successful.

context

A value that will be passed back to the client-side event handler via the context parameter.

clientErrorCallback

The name of the client-side event handler that will receive the results of the server event if an error occurs.

target

The ID of the control to which the callback should be sent if the default control is not required.

Parameters for these signatures are shown in Figure.

Listing 9.7 shows how to get the reference and register it as a script block.

Generating the Client Callback
Dim refscr As String = _
  Page.GetCallbackEventReference(Me, "arg", "MyCallBackHandler", _
                                 "ctx", "null")

Dim scr As String = _
  "function CallTheServerCallBack(arg, ctx) {" _
  & refscr & "; }"

Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
  "CallTheServerCallBack", scr, True)

The first line constructs the actual line of client script that will generate the callback. For example, if this is run from the Page_Load event of an ASP.NET page, it will generate:

__doCallback('__Page',arg,MyCallBackHandler,ctx,null)

These parameters are:

  • __Page, which is the current page

  • arg, which is the string to be passed into the server RaiseCallbackEvent

  • MyCallBackHandler, which is the name of a client script to receive the callback from the server

  • ctx, which is the string to be passed directly through to the MyCallBackHandler in the context parameter

  • null, indicating that no error callback function is required

This __doCallback function needs to be hooked into a client-side event to trigger the callback. This can be either directly triggered (such as from the onClick event of a button) or wrapped in another function, which is what the following line does:

Dim scr As String = _
  "function CallTheServerCallBack(arg, ctx) {" _
  & refscr & "; }"

This simply turns the callback routine into:

function CallTheServerCallBack(arg, ctx)
{
  __doCallback('__Page',arg,MyCallBackHandler,ctx,null)
}

The final part of the required action is to register this block of client script:

Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
  "CallTheServerCallBack", scr, True)

At this stage you now have a client script function that will perform the callback. The server implementation of RaiseCallbackEvent will process the argument and return its data back to MyCallBackHandler. Figure on the next page shows a new copy of the client callback architecture diagram to make things clearer.

6. Client callback architecture with code

graphics/09fig06.gif

Client Callbacks in Action

Since the code for this is confusing without a good example, let's look at the entire code for a page called ClientSideCallback.aspx (see Figure).

7. ClientSideCallback.aspx

graphics/09fig07.gif

Entering a value into the top text box and pressing the button performs a client callback—in this case simulating the validation of a zip code. The results are displayed in the lower text area (see Figure).

8. ClientSideCallback.aspx in action

graphics/09fig08.gif

The code for this is shown in Listing 9.8.

Using Client Callbacks
<%@ page language="VB" %>
<%@ implements interface="System.Web.UI.ICallbackEventHandler" %>

<script runat="server">

  Function ClientCallBackFunction(EventArgument As string) As String _
           Implements ICallbackEventHandler.RaiseCallbackEvent

    Return EventArgument & " has been validated"

  End Function

  Sub Page_Load(sender As object, e As EventArgs)

    Dim refscr As String = Page.GetCallbackEventReference(Me, "arg", _
                           "MyCallBackHandler", "ctx", "null")
    Dim scr As String = "function CallTheServerCallBack(arg, ctx){ " _
                        & refscr & "; }"
    Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
                      "CallTheServerCallBack", scr, True)

  End Sub

</script>

<html>
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
  <form runat="server">
    <script language="javascript">
      function CallTheServer()
      {
        var zc = document.forms[0].elements['zipcode'];
        CallTheServerCallBack(zc.value, zc.id);
      }
      function MyCallBackHandler(result, context)
      {
        l = document.forms[0].elements['resultlabel'];
        l.value = "Info: " + result + ' - extracted from ' + context;
      }
    </script>
    <input type="text" id="zipcode" name="zipcode"></input>
    <button id="myBtn" onclick="CallTheServer()">Validate</button>
    <br />
    <textarea id="resultlabel" name="resultlabel"
          rows="10" cols="60"></textarea>
  </form>
</body>
</html>

The Page_Load event is exactly as we've discussed in earlier sections—it simply constructs the CallTheServerBack function, which wraps the __doCallback function.

The server ClientCallBackFunction event simply returns a string—in a real application this perhaps would perform some sort of data lookup.

On the client side there is a button with an onClick event procedure, which takes the value from the top text box (zipcode) and the id and calls the server callback routine with those values. Giving the CallTheServer CallBack function two parameters allows for a flexible approach, where the value and context can be passed in. You could also do this for the callback functions if required.

Finally, the MyCallBackHandler function just accepts the result and context from the callback handler and displays them in the text area.

You can see that although there are several steps in this code, it's actually quite simple. This example uses an ASP.NET page, but it's just as easy to put the implementation into a custom server control. Although this technique relies on XmlHTTP, it is cross-browser friendly and works on Netscape Navigator (see Figure).

9. Client callbacks on Netscape Navigator

graphics/09fig09.gif

Uses for Client Callbacks

Client callbacks are perfect for those situations where you want to provide quick access to server functionality—this approach avoids the rendering phase for a page or control. This could be used for validation, preloading of data, or even timed execution of server code to simulate a push scenario.

Another perfect situation for the use of client callbacks is to solve a problem discussed earlier in the book, in Chapter 7's Allowing User Selection of Skins subsection. There we discussed the problem of a theme browser that would allow users to select a theme to see how it looks. The problem was that the server event procedure that resulted from the selection of the theme fires too late for the theme to be applied—remember that themes have to be applied in the PreInit event. With this new technique, selection of the theme can now perform a client callback that sets the theme, for example:

Function MyCallBackEvent(EventArgument As String) As String _
    Implements ICallbackEventHander.RaiseCallbackEvent

  Profile.Theme = EventArgument

End Function

The client callback function can then simply invoke a standard postback:

<script language="javascript">

  function MyCallBackHandler(result,context)
  {
    __doPostBack('btnUpdate',,)
  }

</script>

The real postback will then run the PreInit event, at which stage the theme can be read from the Profile. Now a single selection can change the theme of a page.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows