Friday, October 12, 2012

Code Snippet - Close dialog if all fields are valid

Today I was working on a dialog that had fields with validation. I only want to close the dialog if all fields are valid. I'm not aware of any inbuilt XSP methods that does this. This code snippet checks for any invalid fields in the dialog. If all fields are valid, the dialog is closed.
function closeDialogIfValid( dialogId ){
 var invalidCount = dojo.query( '[id="' + dialogId + '"] [aria-invalid="true"]' ).length;
 if( invalidCount === 0 ){
  XSP.closeDialog( dialogId );
 }
}
This probably only works if you're using server side validation.

If there is something like this in the XSP API, please let me know.

Share and enjoy!

Thursday, September 6, 2012

Snippet to clear session for user

During testing, I sometimes log in as different user to test hide/whens/etc. I used to delete the SessionID cookie in the browser to clear session scoped beans/sessionScope variables. Today, I looked for a solution to automate this. This line will clear all objects related to a session:
facesContext.getExternalContext().getSession( false ).invalidate();
If you want to clear session when logged in user changes for the current "XPages" session, here's the snippet I use (put the code in afterPageLoad or beforePageLoad):
// Reset session when user changes
var currentUserName = sessionScope.currentUserName;
var userName = session.getEffectiveUserName();
if( currentUserName && userName !== currentUserName ){
 facesContext.getExternalContext().getSession( false ).invalidate();
}   
sessionScope.currentUserName = userName;
I would think this is better than deleting the cookie, as the server is immediately notified that it should flush the objects bound to the session. When I delete the cookie, I believe that the objects are kept in memory until a specified timeout.

Monday, August 20, 2012

Comprehensive guide to Design Definitions

I found this guide in IBM's Application Development wiki while looking for information on Design Definitions:
Native and Custom Control Custom Visualization Best Practices

So far it's the most comprehensive guide to Design Definitions I've found.

Friday, July 27, 2012

XPages - Grouping data in comboboxes

I wanted to have some values grouped in a ComboBox. Not sure if it's possible to do with pure SSJS (without using the Java API), but found a way using beans:
JSF Tree in a dropdown (see accepted answer).

The resulting html is select node with options grouped in optgroup nodes.

Monday, June 25, 2012

Multi value fields and Beans in XPages

I had an issue with multi value fields bound to bean fields in an XPages I worked on.

I got this error message on refresh:
java.lang.IllegalArgumentException: argument type mismatch

The issue turned out to be a somewhat inconsistent underlying API. When the field is empty, or has single value, it tries to set a string. When there are multiple values, it tries to set a list.

To work around the issue, have the getter return an Object, and the setter accept an object.
E.g.

public Object getInputMulti() {
 return this.inputMulti;
}
...
@SuppressWarnings( "unchecked" )
public void setInputMulti( Object inputMulti ) {
 if( inputMulti == null ){
  this.inputMulti = null;
 }
 
 if( inputMulti instanceof String ) {
  Vector inputNameList = new Vector();
  inputNameList.add( (String) inputMulti );
  this.inputMulti = inputNameList;  
 }
 if( inputMulti instanceof List ) {
  this.inputMulti = (Vector) inputMulti;
 }
}

You can have the translation for the setter be done in a utility method. E.g.
@SuppressWarnings("unchecked")
public static Vector translateToVector( Object object ){
 if( object instanceof String ){
  Vector list = new Vector();
  list.add( object );
  return list;
 }
 
 if( object instanceof List ){
  return (Vector)object;
 }
 
 return null;
}

Then, for the setter:

@SuppressWarnings( "unchecked" )
public void setInputMulti( Object inputMulti ) {
  this.inputMulti = UtilityClass.translateToVector( inputMulti );
}

Example of multi value fields: Checkbox group, multi value fields (fields with multipleSeparator), list boxes.

Friday, June 22, 2012

Update for Enhanced Messages Control - Show "system" messages

In response to this question on the XPages Forums, I've updated the source code for my Enhanced Messages Control, so that it also shows messages not bound to controls.

The "system" messages are show above the messages from components.

Friday, June 15, 2012

Recommended tutorials for doing asynchronous processing in beans

Asynchronous processing in Java applications – leveraging those multi-cores

Using asynchronous mechanisms in Java and JavaScript for improving the user experience

Friday, June 8, 2012

Update for Enhanced Messages - Now in page order

I added a small snippet of code that sorts the messages in the order that the components are in the page.

I only had a simple page to test on, but in theory it should work for large pages with complex structure.

Original post with source code for custom control

Thursday, May 31, 2012

Improving performance in Domino Designer when developing XPages

If the XPage has a view data source/the view name is a static value, it looks like Domino Designer is constantly polling the view for column info/etc.

If you are done dragging and dropping columns from the view to the view panel/etc. Go into source mode and compute the viewName.

Before

<xp:dominoView var="view1" viewName="MyView" />

After

<xp:dominoView var="view1" viewName="${javascript:return 'MyView'}" />

When the view name is computed, Domino Designer can't determine what view it should check -> No more lag.

I haven't tested this with Document Data source, but you might get a performance boost if you compute the formName as well, as Domino Designer won't have to check the fields for drag/drop.

Optimally I would wish the Designer team implemented a refresh fields/columns button for the data pane.

Share and enjoy!

Tuesday, May 29, 2012

Using Java reflection and Domino form to auto populate beans

>> Download DemoApp

Due to a lot of positive posts about using beans/MVC pattern for developing XPages apps, I've started writing more Java and less SSJS. I really like the performance improvements/stricter structure of code.

In one of my recent apps, I wrote a Model class for each of the forms in the app. The class has fields for all the form fields. Whilst writing the app, I thought about using Java reflection/Domino Form to auto populate the class instance. Java reflection lets you fetch methods from classes using their name (as a String). The Form class contains methods that lets you fetch all the declared field names in the form.

A couple of advantages to using auto population of fields is less code to maintain (~less bugs), and "auto discovery" of typos in either field names or getters/setters. If you call a field firs_name (typo), and write getters/setters for first_name, using the methodology I use, the code would crash, and you would instantly be aware of some error. If you use some kind of logging, you could look at the stack trace to determine what failed.

A couple of disadvantages. Using reflection/fetching field information adds a slight performance penalty. I don't notice any slowness, but it's something to be aware of if you're aiming for light speed/serving the entire Republic of China on one server. :)

In most of my apps, I use underscores to separate "words" in my field names. To make this work with reflection I convert the underscore field names to ProperCase, and prepend get/set.

E.g.
Field: first_name
ProperCased: FirstName
Getter: getFirstName
Setter: setFirstName

In the attached DemoApp, I have a helper class that deals with the most common field types. It has two methods:
setObjectFieldsFromDocument( String form, Object object, DominoDocument doc )
setDocumentFieldsFromObject( String form, Object object, DominoDocument doc ).

The first parameter is the name of the form that you want to use as "schema" for the population of the instance fields. The second parameter is the Java instance. The third parameter is a Document wrapped in a simple helper class.

The demoapp has a XPage that uses a controller bean (PersonController) to handle interaction with the model (Person). It's bound to view scope. When the XPage is loaded, it checks for a parameter (id). If id is available, the controller uses setObjectFieldsFromDocument to populate the model. When save is called, the controller uses setDocumentFieldsFromObject to update the underlying Notes Document with values from the model.

If this sounds interesting. Download the Demoapp, look at the Person XPage for the data binding, and use Package Explorer to take a look at underlying java code.

I'll just add the source code for the Person model class, in case some of you fear that everything in Java is complex

package com.dontpanic82;

import java.io.Serializable;
import java.util.Date;

public class Person implements Serializable {
 private static final long serialVersionUID = 1L;

 public static final String FORM = "Person";

 private String id;
 private String firstName;
 private String lastName;
 private String email;
 private String homepage;
 private Double age;
 private Date birthdate;

 public Person() {
 }

 public Double getAge() {
  return age;
 }

 public Date getBirthdate() {
  return birthdate;
 }

 public String getEmail() {
  return email;
 }

 public String getFirstName() {
  return firstName;
 }

 public String getHomepage() {
  return homepage;
 }

 public String getId() {
  return id;
 }

 public String getLastName() {
  return lastName;
 }

 public void setAge( Double age ) {
  this.age = age;
 }

 public void setBirthdate( Date birthdate ) {
  this.birthdate = birthdate;
 }

 public void setEmail( String email ) {
  this.email = email;
 }

 public void setFirstName( String firstName ) {
  this.firstName = firstName;
 }
 
 public void setHomepage( String homepage ) {
  this.homepage = homepage;
 }

 public void setId( String id ) {
  this.id = id;
 }

 public void setLastName( String lastName ) {
  this.lastName = lastName;
 }

}

Thursday, April 19, 2012

XPages Toolbox - Really nice profiling tool

If you feel that your XPage application is too slow, XPages Toolbox can help you to find the biggest bottlenecks in your code.

If you haven't tried it, I highly recommend that you give it a spin (it's free). If you like it, give it a good rating.

+1 to Philippe Riand for sharing it with the community :)

Thursday, March 15, 2012

Showing horizontal notes data as vertical in view

Today at work, I needed to transfer some data from a Notes application to SQL. The documents in question were horizontal.

An example of what I mean:
An order form in Notes with five order lines. For each line, there may be five fields that contains information about the order. Making it a total of 25 fields for five order lines.


Traditionally, if you want to show this data in a regular notes view, you have to have a column per field.


Thanks to the way the index is organized in a Notes view, you can show this data vertically using Show multiple values as separate entities.


A summary of the technique:
  • In each column, create a list of the field values that you want to show
  • Each column list has to have equal amount of values
  • Each column has to have Show multiple.. property enabled
  • Only the first column can be sorted (it can be categorized), or you end up with a lot of rows.
    This is probably due to how the index is organized/matching of multiple values

Here's the demoapp I took the screenshots from:
>> Download

Take a look at the Vertical view to see the technique I used.

Thanks to this technique, I can simply pull the data from a view using view entries/column values/send row by row to a stored procedure in SQL. The alternative would be to write code for each field.

Share and enjoy! :)

Thursday, March 8, 2012

Patch for bug in XSP.partialRefreshGet/-Post in 8.5.3

Someone made a really stupid mistake in the code for XSP.partialRefreshGet/-Post in 8.5.3..

I won't go into specifics of the code as I'm uncertain if it's breaking some license. Let's just say that if they moved a line of code four or five lines upwards, there wouldn't be any bug.

I wrote a simple patch for the bug. Put it in a CSJS library, and your code will work like it did on 8.5.2 (unless I made a stupid mistake).

/**
* Fix for bug with partialRefreshGet/-Post in 8.5.3
*/
(function(){
var oldPartialRefreshGet = XSP.partialRefreshGet;
XSP.partialRefreshGet = function( targetId, options ){
// Convert to array
var argsArray = Array.prototype.slice.apply( arguments );

if( argsArray.length > 1 ){ argsArray[1] = argsArray[1] || {}; }
if( argsArray.length === 1 ){ argsArray.push( {} ); }

oldPartialRefreshGet.apply( XSP, argsArray );
};

var oldPartialRefreshPost = XSP.partialRefreshPost;
XSP.partialRefreshPost = function( targetId, options ){
// Convert to array
var argsArray = Array.prototype.slice.apply( arguments );

if( argsArray.length > 1 ){ argsArray[1] = argsArray[1] || {}; }
if( argsArray.length === 1 ){ argsArray.push( {} ); }

oldPartialRefreshPost.apply( XSP, argsArray );
};
})();


This code should be forwards compatible with future API updates, as it doesn't assume number of arguments in function.

Thursday, January 26, 2012

Runtime optimized JavaScript and CSS - workaround for multiple stylesheets

Update: This workaround is only needed if you use "folders" in the image resource name.
E.g. backgrounds\home.png.

If you have multiple local stylesheets on a page, the CSS files are combined into a single file.

This changes the url to the stylesheet (adds xsp/.ibmmodres/.css after the path to the db).

If you have an image reference like url(image.png), the image can no longer be loaded, as the url is relative to the path of the stylesheet.

To work around this issue, add ../../../ to the image reference.

E.g.
url(../../../image.png)

Thursday, January 5, 2012

Useful tool when working with text files (CSV, source code, etc)

WinGrep is a tool that let's you search one/several folders for parts of strings. It supports searching in zip files, and lets you use Regular Expression (Perl syntax?) for searching strings.

The result pane shows all the matching lines in the file(s) you are searching.

Monday, January 2, 2012

Thing to be aware of when using "Generate unique keys.." in view

I had trouble with a couple of views today. The views had the option Generate unique keys in index checked.

When the views were replicated to other servers, they weren't built. When trying to open them I got the error message Entry not found in index

I found the solution on the Domino forums, add @IsUnavailable($Conflict) to the view selection.