Tuesday, January 12, 2010

Workaround for radiobuttons changing values on page refresh in Firefox

I had some issues with Firefox changing radio button values on page refresh. Found this workaround description.

Monday, January 11, 2010

Further investigation of using FIELD in a Page

I got a little fired up by my previous find on using FIELD for declaring global variables in a Page. Therefore I did a little more digging.

FIELD-declared variables does not leak into embedded views, nor do FIELD-declared variables in view selection leak up to the containing page.

FIELD-declared variables leak down to an embedded outline when you display a page in the Notes Client and on the web, HTML style. If you show the embedded outline using a Java Applet, it doesn't work.

FIELD-declared variables in an outline are globally available to the items below the outline item it's declared in (regardless of indentation), in the Notes Client. They are not globally available for web applications, regardless of HTML/Java rendering. E.g. outline item 1 has a FIELD-declared variable -> every outline item after outline item 1 can use it (Notes only).

Use FIELD in pages to declare global variables

Edit: It only seems to work when the page is set to Content-Type: Notes, which is a drag (needed it for a custom content-type/JS-page)..

What I've probably longed most for in pages is the ability to have global variables. I thought this was impossible due to the lack of fields in a page.

It turns out it's quite simple. The reason I came up with trying this is Andre Guirards article on Using View Column Programmatic Names. In this article (recommended read!), he discusses the usage of FIELD in view columns/selection formulas to create temporary global variables.

To declare a variable as global, put FIELD in front of it.
FIELD globalVariable := "I'm global!";
For cross client (Notes/Web) pages, it seems that you have to put the global variable declarations in "Window Title". First declare your variables using FIELD, then add the code for the window title.

Declaration:


Using the variable in a computed text:


Notes:


Web:

Thursday, January 7, 2010

Bookmarklet that lets you listen to all dojo events

This was originally reported by Ajaxian. The original page with the code has disappeared/or is down.

I found the code for the bookmarklet here.

Bookmarklet

XPages: Hijacking/publishing partial refreshes globally

Update 11.10.2010 Found another bug. onComplete can sometimes be a string. Fixed.

Update 07.05.2010 Fixed a bug where the onStart/onError/onComplete event were overwritten.

This is a modification to Jeremy Hodges Adding the Ability to Watch for ANY Partial Refresh in an XPage.

The difference between this and Jeremy's code is that this lets you have multiple listeners to all stages of the partial refresh (init, start, complete and error).

function hijackAndPublishPartialRefresh(){
// Hijack the partial refresh
XSP._inheritedPartialRefresh = XSP._partialRefresh;
XSP._partialRefresh = function( method, form, refreshId, options ){
// Publish init
dojo.publish( 'partialrefresh-init', [ method, form, refreshId, options ]);
this._inheritedPartialRefresh( method, form, refreshId, options );
}

// Publish start, complete and error states
dojo.subscribe( 'partialrefresh-init', function( method, form, refreshId, options ){

if( options ){ // Store original event handlers
var eventOnStart = options.onStart;
var eventOnComplete = options.onComplete;
var eventOnError = options.onError;
}

options = options || {};
options.onStart = function(){
dojo.publish( 'partialrefresh-start', [ method, form, refreshId, options ]);
if( eventOnStart ){
if( typeof eventOnStart === 'string' ){
eval( eventOnStart );
} else {
eventOnStart();
}
}
};

options.onComplete = function(){
dojo.publish( 'partialrefresh-complete', [ method, form, refreshId, options ]);
if( eventOnComplete ){
if( typeof eventOnComplete === 'string' ){
eval( eventOnComplete );
} else {
eventOnComplete();
}
}
};

options.onError = function(){
dojo.publish( 'partialrefresh-error', [ method, form, refreshId, options ]);
if( eventOnError ){
if( typeof eventOnError === 'string' ){
eval( eventOnError );
} else {
eventOnError();
}
}
};
});
}

Run the above function at the top of your application script to hijack and publish the partial refresh event.

Example usage:
dojo.subscribe( 'partialrefresh-init', null, function( method, form, refreshId ){
alert('Partial refresh for ' + refreshId + ' initiated.' );
} );

dojo.subscribe( 'partialrefresh-start', null, function( method, form, refreshId ){
alert('Partial refresh for ' + refreshId + ' started.' );
} );

dojo.subscribe( 'partialrefresh-complete', null, function( method, form, refreshId ){
alert('Partial refresh for ' + refreshId + ' complete.' );
} );

dojo.subscribe( 'partialrefresh-error', null, function( method, form, refreshId ){
alert('An error occured during partial refresh of ' + refreshId + '.' );
} );

Documentation for dojo.subscribe

Wednesday, January 6, 2010

XPages: Binding customizable scoped variables to fields in custom controls

Edit 13.09.2012: As commenter, Bas van Gestel pointed out. This method of doing things is outdated. I can't remember the reason why I had issues, but this is a better way of doing it (where fieldName is a custom property):
#{viewScope[compositeData.fieldName]}
I stumbled onto a weakness(?) in the XPages editor today. I have a complex field that I use twice in an XPage. The only difference between the two instances of the field is the rendered-property, and the data binding. To avoid copy/pasting(/screwing up in the long run), I decided to make a custom control of the field.

The rendered-property is set on the custom control, instead of the field. The data binding is a scoped variable. I couldn't find a way to compute the name of the scoped variable in the data binding section. To work around this (flaw?), I set the data binding afterPageLoad.

Here's the code:
// Set value-binding when page loads
var application = facesContext.getApplication();
var scopedField = compositeData.scopedField;
var valueBinding = application.createValueBinding( '#{viewScope.' + scopedField + '}')
getComponent( 'text-filter' ).setValueBinding( 'value', valueBinding );
text-filter is the id of the field.
scopedField is the custom property that defines the name of the scoped variable

If you have a custom control that's used many times inside a page, you may need private scoped variables.

Share and enjoy!