Tuesday, April 27, 2010

XPages: Server Side Event Delegation - In view edit

This is a continuation of the previous investigation into the possibilities of Server Side Event Delegation (SSED).

As I mentioned at the end of the previous blogpost, the demoapp didn't take advantage of Client Side Event Delegation (CSED). This resulted in having to create the same amount of client side event listeners as was generated by the server. If you have repeat controls/view events, this can amount to quite a bit of script tags (html to download). The event listeners themselves can take up quite a bit of memory, and may make the page act a little sluggish.

Here's the generated script tags for events in this demoapp:
XSP.addOnLoad(function() {
  XSP.attachPartial("view:_id1:eventDelegator", "view:_id1", null, "foo", ...
  XSP.attachPartial("view:_id1:peopleView:pager1__Group__lnk__1", null, ...
  XSP.attachPartial("view:_id1:peopleView:pager1__Group__lnk__2", null, ...
  XSP.attachPartial("view:_id1:peopleView:pager1__Group__lnk__3", null, ...
  XSP.attachPartial("view:_id1:peopleView:pager1__Group__lnk__4", null, ...
  XSP.attachPartial("view:_id1:peopleView:pager1__Next", ...

One for the global event delegator event handler, and one per link in the view pager. The view has thirty "click to edit" cells, and a couple of buttons from the previous version. Not bad. :)

The experiments into SSED is partially due to the aforementioned reasons, curiosity, and my eternal struggle to find smarter ways to work. Only time will show if it fulfills all of the above.

Since the previous experiment, the demoapp has grown a bit. I've added a couple of more class attributes:
  • delegate-click-parent: makes a component a clickable area
  • click-trigger: when parent has delegate-click-parent class/styleClass, elements with this class, when clicked, triggers the server side event delegator
  • refresh-[refreshId]: previously refresh-target-[refreshId], the target of the partial refresh
  • action-[name]: used by the server side event delegator to determine what action to take
  • data-[value]: can be used on the server side for various things. E.g. unid of a row in a view
The editable columns work like this:
The view has the delegate-click-parent class. This makes all the child elements with a class attribute of click-trigger activate the server side event delegator.

I created a custom column. This lets me put other controls into the column.

Inside the column, I put a panel, a field and a button. A cell is editable when a scoped variable containing unid is the same as the unid of the row that the cell belongs to.

The panel has a computed styleClass attribute. When the cell isn't editable, the panel acts as a click trigger. One of the class attributes is the previously mentioned data-class. This contains the unid of the row the cell belongs to. This unid is put into the previously mentioned scoped variable, making the cell editable.

The field has value="" and defaultValue="#{row.columnName}". This makes it possible to edit the field/have it take the value from the appropriate row/column. The field has a readonly attribute that's computed based upon if the cell is editable.

The button is hidden when the cell isn't editable. When clicked, it triggers a save and resets the editable row. The client side id of the button is passed along to the server. This makes it possible to get the name of the field/get the value from the params-map (contains all submitted values).

component id of field/button -> field/button
client side id of button: view:_id1:button
client side id of field: view:_id1:field

No more than a simple replace to find the correct field name :)

That's more or less it. If this sounds like something interesting, take a look at the demoapp/code, and leave a comment if you're curious about something.

>> Download Demoapp

Share and enjoy!


Anonymous said...

This is a very valuable tip. I am attempting to use it within an application that I am developing. The difference being is that rather than the first column I am using the last column and it is a date-value not a name value.
Where is the reference to the first column so that I can modify to return the value of the last value as required.