Wednesday, September 29, 2010

Storing data as JSON in a field VS multiple fields

I've started to convert a CMS I'm maintaining at work to XPages. I'll probably do it over a longer time span, when I'm not doing other more important work. In the process, I'm brainstorming a little bit on how to improve some of the things in the application.

One of the things our customers request is more fields for links/the ability to sort them (they are displayed in "categorized" sections on the right side of the page in read mode). The form currently have 20 rows of links. There are five fields for each link (hidden?, category, title, url, target). This takes up quite a bit of real estate on the form.

In the XPages version, I'm considering changing the way links are stored. I'm thinking about storing them in a single field as JSON. I've made a list of pros/cons. There's no traditional Notes interface for the app, so I don't have the need for the values to be "notes view compatible".
Pros
Less fields to deal with
Takes up a lot less real estate on the form/xpage
Easy to change the order of the link values
Cons
Adds processing overhead(?), as the JSON has to be converted from/to JS objects when storing/retrieving
More complex
Needs a widget/logic to add/edit/remove
Currently, I'm in favor of implementing the change. I might implement this for other "repeating" fields as well if it works out well. If you can see any more pros/cons, please feel free to leave a comment :)

XPages: SSJS code snippet that lets you parse/stringify JS<->JSON

Update: I got word from Philippe Riand that fromJson/toJson/isJson is implemented in the XPages runtime. Apparently it has been available from 8.5.1.

Example usage:
var json = toJson( {a:[1,2,3]} ) //-> '{"a":[1,2,3]}'
var jsObj = fromJson( '{"a":[1,2,3]}' ) //-> {a:[1,2,3]}
isJson( '{"a":[1,2,3]}' ) //-> true
---

A while back, I ported a script written for Rhino to SSJS that lets you parse JSON to JS objects/serialize JS objects to a JSON string. I've found it quite useful, so I thought I should share it with you.

I can't remember where I found the Rhino script, but the original source code that the Rhino script is based on is linked to at the bottom of this page: http://www.json.org/js.html

The original script was written/maintained by Douglas Crockford, and should prevent script injection, e.g. database.getAllDocuments().removeAll(true).

The script


Share and enjoy!

Monday, September 20, 2010

If you're getting errors on dijit.Dialog.hide() on Domino 8.5.2 - workaround

Update: I found the reason. Opening a dialog from another dialog (which is then destroyed). Unless refocus=false, dojo (1.4.3) tries to put back the focus to the "destroyed" dialog.

There is a bug in the current code stream (dojo 1.4.2) when hiding/closing dialogs. I'm not a 100% sure what triggers the bug in our applications. It happens on a couple of dialogs.

The workaround for the bug is to add refocus: false in the options when creating the dialog. E.g.
var dialog = new dijit.Dialog({
refocus: false,
title: 'Dialog title'
});

Tuesday, September 14, 2010

XPages: Workaround for fields losing focus on partial refresh

Update 13.08.13: I found a bug when a link triggered partial refresh. Fixed.

Update 20.03.13: I updated the code so that it doesn't try to focus the field if the partial refresh hasn't influenced the active field.

Update 02.05.11: By request, I made a simple demoapp.

Update 22.11.10: Ajit Rathore found a weakness in the code. On partial refresh error -> unsubscribe to the event. I've added his patch to the code.

The code snippet below, in conjunction with my hijackAndPublishPartialRefresh function should fix the problem of fields losing focus when elements containing them are refreshed.
dojo.addOnLoad(function(){
dojo.subscribe( 'partialrefresh-init', function(){
 // setTimeout needed to make it work in Firefox
 setTimeout(function(){
  var activeElementId = document.activeElement.id;  

  var focusSubscription = dojo.subscribe( 'partialrefresh-complete', function(){
 // Only set focus if field hasn't been overwritten/lost focus
 if( document.activeElement.id !== activeElementId ){
  var activeElement = dojo.byId(activeElementId);

  if( activeElement && /INPUT|SELECT|TEXTAREA/.test( activeElement.nodeName ) ){
   // Set focus to element/select text
   activeElement.focus();
   if( activeElement.nodeName !== 'SELECT' ){
    activeElement.select();
   }
  }
 }
  
   // Unsubscribe after focus attempt is done
   dojo.unsubscribe( focusSubscription );
  });

  // In case of error -> remove subscription
  var errorSubscription = dojo.subscribe( 'partialrefresh-error', function(){
     dojo.unsubscribe( focusSubscription );
  });
 }, 0 );
} );
});
When a partial refresh is initiated, the browser is queried for the active element/the id of the element is stored in a variable. When the refresh is complete, focus is set to the element that had focus before the refresh.

I've only tested it in a small testpage, so there may be bugs that I'm not aware of. Please let me know if you encounter any.

I've tested it/it should work in IE7/8, latest version of Firefox and Opera.

Share and enjoy!

Monday, September 6, 2010

XPages: Helper function for inconsistent API methods - always get an array

Update: I added functionality to convert collections (ArrayList/Vector/etc.) to arrays as well.

// Helper for inconsistent API
// Wrap around @DbLookup/@DbColumn/@Trim/@Unique calls to have an array returned
function $A( object ){
// undefined/null -> empty array
if( typeof object === 'undefined' || object === null ){ return []; }
if( typeof object === 'string' ){ return [ object ]; }

// Collections (Vector/ArrayList/etc) -> convert to Array
if( typeof object.toArray !== 'undefined' ){
return object.toArray();
}

// Array -> return object unharmed
if( object.constructor === Array ){ return object; }

// Return array with object as first item
return [ object ];
}
E.g.
@Unique( 1, 1, 1 ) -> 1
$A( @Unique( 1, 1, 1 ) -> [ 1 ]

@Trim( '', '', 'a', '' ) -> 'a'
$A( @Trim( '', '', 'a', '' ) ) -> ['a']

If IBM fix this inconcistency, your code will not break if you're using the above function. Call it whatever you like. I called it $A from the $A function in MooTools.

Share and enjoy!

Friday, September 3, 2010

If you want to run XPages API Inspector in the Client

Add this line to your java.policy file (in the notes directory):
grant {
permission java.lang.RuntimePermission "accessDeclaredMembers";
};
Thanks to F. Kranenburg for the tip. :)

Need help.. ECL Exception when using Java Reflection API

Update: Workaround

I got a feature request for my XPages API Inspector. When running the inspector on an XPage in the Notes Client:
Exception:ECL Permission Denied (java.lang.RuntimePermission accessDeclaredMembers)

I think it has something to do with accessing declared members of a class. Have any of you encountered this, and if so, is there a way to work around it in code, or is this something that you have to do on the client/server?

Thursday, September 2, 2010

XPages API Inspector V3

Changes:
From feature requests (thanks, Mark Leusink, F. Kranenburg):
* Classes that don't have a constructor can now be inspected (e.g. lotus.domino.local.Item)
* The API Inspector now has partial refresh. No more full reload of page
* Possibility to filter hidden _components (e.g. eventHandlers)

My own tweaks:
* Removed the clear-button
* If you select something other than Custom, the input field/inspect button is hidden.
To reset, select custom
* The expression field is now an auto-growing text-area.
This allows you to test more advanced expressions in a more user friendly way.
>> Download V3 from OpenNTF.org

Let me know if there are any bugs :)