Rick Strahl's Weblog
Rick Strahl's FoxPro and Web Connection Weblog
White Papers | Products | Message Board | News |

Mixing Client and Server Events in Web Connection 5.0


April 24, 2006 •

There have been a number of questions regarding client events and server events in Web Connection 5.0, specifically in relation to the various button controls that come with Web Connection 5.0. First a little background.

 

Button controls can fire events back to the server into a Web Connection page class. So if you have a wwWebButton you can have a click event which is mapped into the event that you specify like this:

 

<ww:wwWebButton ID="btnSubmit" runat="server" click="btnSubmit_Click" Text="Edit Entry"

                Width="100px" />

 

You then implement btnSubmit_Click on the generate wwWebPage class that exists on the server and all is well. You have access to other controls you can access a database and manipulate the page as needed on the server.

 

So that's the standard Web Connection server event model – there's an event (click in this case) and it fires back on the server based on the name of the method you specify in the click attribute.

 

All of this is actually mapped through HTML constructs and it's important to understand how HTML works in this respect. Most importantly, HTML only includes a single control that can officially submit a form and that's submit button (<input type="submit">). Any other control that posts back to the server has to fake this behavior using some other mechanism.

 

In Web Connection there are a number of controls that can 'AutoPostback' to the server: Linkbuttons, ImageButtons, Checkboxes, Listboxes and Dropdownlists to name a few. Custom controls you create on your own also can use the same mechanism. None of these controls post back natively so we need to coerce them to do this by using some client script. Web Connection (as it borrows from ASP.NET's approach) uses a client function and a hidden variable to identify which control and action initiated the event. Behind the scenes this looks something like this:

 

<form id="form1"  action="" method="POST">

<input type="hidden" id="__EVENTTARGET" name="__EVENTTARGET" value="" />

<input type="hidden" id="__EVENTARGUMENT" name="__EVENTARGUMENT" value="" />

<input type="hidden" id="__EVENTPARAMETER" name="__EVENTPARAMETER" value="" />

 

<script type="text/javascript">

<!--

var theForm = document.forms['form2'];

if (!theForm) {

    theForm = document.form1;

}

function __doPostBack(eventTarget, eventMethod,eventParameter) {

    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {

        theForm.__EVENTTARGET.value = eventTarget;

        if (eventMethod)

           theForm.__EVENTARGUMENT.value = eventMethod;

        if (eventParameter)

            theForm.__EVENTPARAMETER.value = eventParameter;

        theForm.submit();

    }

}

 

//-->

</script>

 

And then for the actual postback implementation (in this case a wwWebImageButton):

…

<a href="javascript:__doPostBack('Panel_WwWebImageButton2','click')" id='Panel_WwWebImageButton2'><img src="/wconnect/weblog/images/warning.gif" id="Panel_WwWebImageButton2_Image" style="border-width:0px;" ></a>

 

The __doPostback functionality fires the JavaScript function, it encodes the event parameters which can then be picked up on the server by the Web Connection page framework to route the event to the appropriate event handler.

 

It's fairly complex when you look at all this generated code, but it's all handled for you automatically by the framework behind the scenes so all you do is drop a control and it just all works.

Server and Client events

The above concerns itself with Server side events – the action on the client is mapped to a server side event that fires and causes server code to execute, which is the most common application scenario.

 

But what if you want client side behavior instead. This is where things get a little bit more complicated for you as the developer as the rules for this behavior vary depending on which control you deal with because the various HTML controls that are emulated simply are very, very different.

 

wwWebButton

Remember the Html Submit button is the only control that can submit a form so it's special. Buttons can be in two modes which are determined using the UseSubmitBehavior property. If .T. the button is a submit button otherwise it's a client button that doesn't post back to the server. If you want only client behavior set this property to .F. if you want a server post set it to .T.

 

What if you want both? While you can specify onclick or onclientclick on a submit button and the onclick code will run that code cannot stop the button from posting back to the server. A submit button always posts to the server.

 

The only way to abort a server submit is to implement the form's onsubmit handler and return false from it. But from within button click code you cannot abort.

Other Controls

As mentioned all other controls that post back require the use of the __doPostBack() function to fire events back to the server. These calls are hooked up to the appropriate events like onclick on a button, onchange on lists etc. What this means is that if you hook up an onclick event of your own to controls that are set to AutoPostback these events are going to be thrown out and won't fire. It won't work.

 

So how can you do this? There are a couple of ways.

 

Call __doPostBack() on your own

You can set up your controls to not be autopostback and instead manually force them to post back by calling __doPostBack from your own script code. By doing so you get full control over the postback process.

 

<script>

function lnkClick(Option)

{

   if (Option)

      return false;

 

   // Fire Click event on the server

   __doPostBack('<%= this.lnkClick.UniqueId %>','click');

 

  return true;

}

<script>

 

Note that if you don't AutoPostback set and no other control on the page is posting back the __doPostBack JavaScript block may not get generated. To ensure this does happen add the following to your startup code:

 

FUNCTION OnLoad()

 

   THIS.Page.RegisterPostbackScriptcode()

 

ENDFUNC

 

Move your Validation code to Form.onsubmit()

The above works but it takes a bit of inside knowledge. A better way that is less obvious is to override the form's onsubmit() behavior on the client and make any decisions on whether you want to submit there:

 

<form id="form1" runat="server" method="POST" onsubmit="form_submit()">

<script type="text/javascript">

function form_submit()

{

    var Ctl = document.getElementById('<%= this.txtName.UniqueId =%>');

    if (Ctl.value == "")

    {

        alert("Name can't be blank");

        return false;  // dont submit

    }

    return true;

}

</script>

… rest of the HTML page

 

Now you can still use submission from individual control events, but the actual check is performed in script code.

 

A note about submitting non Submit Buttons

Note that if you use non-submit buttons, those buttons cannot fire click events to the server automatically. A non submit button doesn't post to the server period so if you want to make a button submit to the server you have to either force the form to submit explicitly (document.Form1.submit();) or fire an event of another control that can submit (document.getElementById('btnSubmit').click() ).

 

If you fire a submit realize that the submit from a non submit button will not trigger a server event. It will fire the page but no event will be triggered as there's no event id associated with the button. Buttom line is that if you need a button click to fire against the server and you need a specific event fired, use a submit button.

 

It ain't trivial

As you can see there are quite a few complexities and combinations in this scenario of mixing client and server events. My general guideline is this:

 

If you use client events don't overlap with server mapped events. Use client events or server events on a particular event like click or change, but not both. If you need both decide which is more important and make that the target of your server control, then use the approaches above (using explicit postbacks or onsubmit overrides) to handle the client part of the process.

 

Hope this provides some reference for anybody who's run into these issues.

Posted in:

Feedback for this Weblog Entry