Tuesday, February 16, 2010

XPages: Making dojo dialogs works with server-side events

Julian gave me a link to a cleaner way of making dialogs work. I recommend using that method instead of what's below.

As I've mentioned several times, I'm currently working on a larger project that relies heavily on XPages technology.

One of the widgets I felt the need for in this project is the modal dialog box. Dojo has two inbuilt widgets that provides this functionality, dijit.Dialog, and dojox.widget.Dialog.

As Julian Buss points out, you'll stumble onto a couple of big problems when using these widgets in XPages (out of the box). Server side events aren't triggered, and field values aren't posted to the server. I've used more time than I want to admit, trying to find out why server side events doesn't work. The answer is quite simple.

XSP.getForm

Actions that triggers server side code (partial-/full refresh) need to know which <form> to post. When an event is triggered, the DOM tree is traversed upwards until a form is found/the top of the tree is reached. If a form isn't found, the event is ignored. The dialog widgets in Dojo are moved to the bottom of <body>, outside any form tags.

Client side JS to "fix" XSP.findForm:
/*
Modifies XSP.findForm so server side events are fired correctly when
dojox/dijit dialogs are used.
- First try the native findForm-method to find the correct form
- Then try to find a parent element with the attribute form_id (used specifically in ccModalDialogWeb in this demoapp)
- If everything else fails, return the first form on the page.
*/
dojo.addOnLoad(function( el ){
var oldFn = XSP.findForm;
XSP.findForm = function( el ){
var form = oldFn.apply( this, arguments );

if( !form ){ // Look for element with form_id attribute
el = (typeof el === 'string' ) ? dojo.byId( el ) : el;
if( el && el.nodeName ){
while( el && el.nodeName !== 'BODY' ){
var formId = el.getAttribute( 'form_id' );
if( formId ){ // form_id found -> get form by id
form = dojo.byId( formId );
break;
}

el = el.parentNode;
}
}
}

return form || document.forms[0];
}
});

Only server side events from the the dojo dialog (/any other elements triggering server side events outside forms) are affected by this code. First the original XSP.getForm is tested (so that "standard" events arent't broken). If no form is found, the DOM tree is traversed upwards looking for an attribute, form_id, which can be added to one of the parent elements of the dialog content. If form_id is found, it's used to locate the correct form. If that also fails, the first form on the page is used. I use form_id in the demoapp. This is not a native attribute, so you have to generate this yourself. getForm.getClientId(facesContext) returns the browser-id of the parent form.

The above code doesn't fix the problem with fields being moved outside the form. In the demoapp, I've made a simple workaround for this (only for input-fields). Basically you have to clone the fields in the dialog, and copy them onto the correct form. Add an onChange event to the dialog fields, so that the clones get the correct value.

If you decide to implement the custom control in your own projects, and need workaround for other field types, let me know.

The demoapp contains a custom control that let's you open another custom control/xpage inside another XPage. If you use a custom control for the dialog, the custom control should have full access to the XPage that contains the dialog.

The interactive part of the demoapp is extremely simple. The "gold" is the custom control ccModalDialog, and the client side script, ccModalDialog.js.

Hopefully IBM implements a standard control for modal dialogs. It's not impossible to make perfect. It only requires time, knowledge and patience.

>> Download demoapp

Share and enjoy!

9 comments:

Qtzar said...

There are some interesting reasons as to why it is moved outside the form as detailed in this dojo bug : http://trac.dojotoolkit.org/ticket/3300

Tommy Valand said...

The ticket you mention was actually a big clue in my search to find out what the problem with dialogs and server side events was.

Julian Buss said...

Mark Hughes solved the problem another way, I wrote about it in the xpageswiki.com at http://xpageswiki.com/web/youatnotes/wiki-xpages.nsf/dx/Work_with_Dojo#Use+a+panel+as+dijit+Dialog

Tommy Valand said...

Thanks Julian.

That seems like a lot less hacky way to do it. Do you know of any weaknesses?

I see he's using the first form on the page. This will work in most cases, but from what I understand, XPages lets you use multiple forms on a page? Either way, that's easy to fix.

Julian Buss said...

I used it in some XPages for the Notes client, and so far I didn't had any problems.

jay said...

I am using Julian's aproach and the dialog seems to work fine. I have an xpage with fields and a button to open a dialog box. In the dialog box I have a field - to be used only to add a value when needed. Problem is when I try to submit my form on the xpage, the field on the dialog box is also getting validated even though the dialog box is hidden. How can I prevent this?

Tommy Valand said...

Take a look at this blogpost.

In the validation of the field in the dialog, write the validation so that it only runs the validation when the button in the dialog is clicked, or something along those lines.

Anonymous said...

Hi - I would like to create a response doc within a dialog box. Is this doable? If so, how can I do this using the similar format you did in this post?

Anonymous said...

Hi - I want to create a response document and have it open as a dialog box. Is this doable using the same procedure as you did? Thanks.