Update 13.01.23 Added Java code neeeded to check what component triggered submit in response to
this question on Stack Overflow.
Java version:
@SuppressWarnings( "unchecked" )
public static String getParameter( String name ) {
Map parameters = resolveVariable( "param", Map.class );
return parameters.get( name );
}
@SuppressWarnings( "unchecked" )
public static UIComponent getChildComponent( UIComponent component, String id ) {
if( id.equals( component.getId() ) ) {
return component;
}
Iterator children = component.getFacetsAndChildren();
while( children.hasNext() ) {
UIComponent child = children.next();
UIComponent found = getChildComponent( child, id );
if( found != null ) {
return found;
}
}
return null;
}
public static T resolveVariable( String name, Class typeClass ) {
Object variable = ExtLibUtil.resolveVariable( FacesContext.getCurrentInstance(), name );
return variable == null ? null : typeClass.cast( variable );
}
public static UIComponent getComponent( String id ) {
return getChildComponent( (UIViewRootEx) FacesContext.getCurrentInstance().getViewRoot(), id );
}
public static boolean wasSubmittedByComponentId( String componentId ) {
if( componentId == null ) {
return false;
}
String eventHandlerClientId = getParameter( "$$xspsubmitid" );
if( eventHandlerClientId == null ) {
return false;
}
// Extract the component id for the event handler
String eventHandlerComponentId = eventHandlerClientId.replaceFirst( "^.*\\:(.*)$", "$1" );
UIComponent eventHandler = getComponent( eventHandlerComponentId );
if( eventHandler == null ) {
return false;
}
// Fetch the component the event handler belongs to
UIComponent submissionComponent = eventHandler.getParent();
if( submissionComponent == null ) {
return false;
}
return ( componentId.equals( submissionComponent.getId() ) );
}
Update 06.10.10 Slimmed the code
Validation in XPages is sometimes extremely hard to work with. Especially when you have partial updates on the page.
While looking for a way to make it easier to work with partial updates on a page with validation, I stumbled onto
this blogpost (JSF). It describes calculating the
required property to determine when the validation should execute.
While we can't apply the technique discussed in the above blogpost, we have another tool at our hands,
$$xspsubmitid. This field contains the id of the event handler that triggered the update.
I wrote a function that lets you test if a specific component triggered an update.
// Used to check which if a component triggered an update
function submittedBy( componentId ){
try {
var eventHandlerClientId = param.get( '$$xspsubmitid' );
var eventHandlerId = @RightBack( eventHandlerClientId, ':' );
var eventHandler = getComponent( eventHandlerId );
if( !eventHandler ){ return false; }
var parentComponent = eventHandler.getParent();
if( !parentComponent ){ return false; }
return ( parentComponent.getId() === componentId );
} catch( e ){ /*Debug.logException( e );*/ }
}
If you only want the validation to run when the user clicks a specific button, write this in all the required-attributes:
return submittedBy( 'id-of-save-button' )
id-of-save-button is the id of the component that triggers a save.
The above code results in the validation only executing when the document is saved. No more broken partial updates.
The downside to this technique is that you have to compute all required-attributes, but I personally think that's a small price to pay to have the XPage behave as expected.
Update:
Julian Buss posted a very valid question. What about the other validators? I did a little test, and here's what I came up with.
For the other kinds of validators, you should be able to use an if-statement to conditionally execute the validator. I took a look at the generated java code for the validators, and from what I can tell, you can put JavaScript statements inside all of them.
E.g. for constraint validators:
if( submittedBy( 'id-of-save-button' ) ){ return /\d+/; }
For big expression statements, it's probably better to do something like this at the top of the script:
if( !submittedBy( 'id-of-save-button' ) ){ return true; }
Share and enjoy!