Thursday, April 22, 2010

XPages: Server Side Event Delegation

Update 27.04.10: I've updated the demoapp with In View Edit functionality

While working on a view with in view edit functionality today, I was appalled over how much client side code was generated.

I guess my subconsciousness was working on the problem, as I came up with a hack that lets you delegate events, while eating dinner (almost forgot the comma). It's somewhat similar to the event delegation that you would do in a browser. The current implementation has its definite limits, but I think it's a nice proof of concept.

Without the Programmatically Triggering an XPages Server Side Event Handler from a Client Side Script article by Jeremy Hodge, I don't think I would have come up with this hack, so thank you for sharing, Jeremy!

The short and simple explanation of the hack:
A global event handler is added to the XPage:
<xp:eventHandler id="eventDelegator" event="foo"
  action="#{javascript:delegateEvent()}" submit="true" refreshMode="partial"
  refreshId="#{javascript:param.$$xspsubmitvalue}" />

I use foo as the event name, so that the generated client side event handler has no possibility of being triggered.

I've added some client side JavaScript that acts upon elements with certain class attributes.
delegate-click: Clicks on an element with this class triggers the server event handler.
refresh-target-componentId: The component id of the control that should be updated.

The code could easily be extended to handle other events. Out of curiosity, I tested onmouseover and onfocus. Both work great.

When an event is triggered, param.$$xspsubmitvalue is set to the id of the component I want to update. As you can see in the above XPages source code snippet, the refreshId of the event is computed to this value. This means that only the requested component is updated. The eventDelegator-function that is triggered by the event uses also uses this value to determine what server side action should be executed.

In the attached demoapp, a viewScope value is altered depending on the refreshId. This toggles a couple of panels, which have computed their visibility based upon the scoped variable.

As for the limitations..

The component that is the target of the refresh has to be visible, as its generated id (clientId) is necessary to get the partial update to work.

This version of the hack doesn't do much about the problem of a lot of active event listeners for my "in view edit" problem. It only results in less generated code. To fix this, I'd have to make the client side code smarter. E.g. add client side event delegation. The container could have a delegate-child-clicks class. The children that should fire the events could have maybe have a click-trigger class. Anyways, that's for a future proof of concept.. :)

The reason I decided to post this experiment is that I hope that it can inspire other XPages developers to make something cool, as I feel Jeremy's post did for me.

Here's the demoapp for you to probe and inspect

Share and enjoy!