Showing posts with label Validation. Show all posts
Showing posts with label Validation. Show all posts

Tuesday, October 19, 2010

XPages: Show validation errors for multiple/specified components

There's a lot of great stuff in XPages, but there's also a few things I miss. One of those things is message-controls that you can connect to multiple fields. It's quite easy to emulate this using a little bit of SSJS, and a xp:text.

SSJS:
// Fetch messages for specified components
function getFacesMessages( components ){
try {
if( typeof components !== 'array' && typeof components != 'object' ){ components = [ components ]; }
var clientId, component, messages = [], msgIterator;
for( var i = 0; i < components.length; i++ ){
component = components[i];
if( typeof component === 'string' ){
clientId = getClientId( component );
} else {
clientId = component.getClientId( facesContext );
}

msgIterator = facesContext.getMessages( clientId );
if( !msgIterator ){ continue; }
while( msgIterator.hasNext() ){
messages.push( msgIterator.next().getSummary() );
}
}
return messages;
} catch( e ){ /*Debug.logException( e );*/ }
}

XPages source code example:
<xp:text styleClass="xspMessage" 
escape="false" rendered="#{javascript:return ( this.value );}">
<xp:this.value>
<![CDATA[#{javascript:return getFacesMessages( [ 'field1', 'field2' ] ).join( '<br />' );}]]>
</xp:this.value>
</xp:text>
Share and enjoy!

Monday, March 8, 2010

XPages: Making validation behave properly

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!

Tuesday, January 26, 2010

XPages: Custom Control that can help prevent save conflicts

Today, I share with you a demoapp with a basic "has document been modified validation" (in lack of a better description) custom control, ccValidateModified.

Just pop it into an XPage with a document data source, and it should in theory work out of the box. Plug and play.

This is by no means an absolute protection against save conflicts. For applications that it's critical to avoid save conflicts, you need better protection.

Below is a movie of the control in action. A document is open in Opera, Firefox and Internet Explorer. When the document is saved in one of the browsers, the "other browsers" gets a dialog saying that the document has been modified.


(Click the play-icon to start. Broadband recommended.)

The way it works is that a scoped variable with the modified-time from the stored doc is set on page load (editmode).

When the window gets focus/every two seconds (setInterval), a partial refresh is initiated. If the doc has been modified since the document was opened for editing, a hidden image is loaded. The image has an onload event (easiest way to make it cross browser) -> show a modal dojo dialog.

>> Download demoapp with custom control

Share and enjoy!

Wednesday, February 28, 2007

Cross client (Web/Notes) validation using JavaScript

Show n' Tell Thursday

Update 02.03.07

I've been working on a simple method for cross-client validation ever since I discovered how powerful the JS-methods in the Notes client was. Of course, it can't compare to the JS-engines in the web-browser, but you can still have a lot of (geek-) fun with what's available.

Since this is a Show'n Tell Thursday post, it's only reasonable to post the validation-demo database for people to play with.

If you just want to se how it works, I've prepared two flash-animations demoing how it looks in Notes, and how it looks on the web.

In the Notes client

On the web

For the web, I'm using Dexagogo's lovely validation library. I've also overloaded Notes' _doClick function so that it only runs if the form is valid (look in the database for the code).

In notes, the DOM is too simple to be able to do any real animation. I've made a simple "yellow fade". To achieve this, and to always have the error-messages available to view for the user, I've had to use a frameset. The validation script in notes is pretty crude.

The script that both Notes and Web use

And finally, the database. In Notes, the demo is the "WithValidation" frameset. On the web, open the "FramedValidation" form (forgot to rename the form to a better name before I uploaded the zip). FramedValidation is also used in Notes as the main form.

Other fun things you can do with JS in the Client:

Run LotusScript by triggering Entering/Exiting events
document.forms[0].fieldName.focus();
document.forms[0].fieldName.blur();


Use LS libraries (JS2LS anyone?)

To do this, fill value of field equal to a function-call (e.g. ..value="Call refreshForm()").

In the Entering event on the field, execute the value. Or, you could have a field for the method you're after, and just focus() on the field. You can hide the field with a layer (put it in a subform to make it re-usable).

Fill data in fields across framesets
FrameName.do...rms[0].fieldName.value="value";

Fill data into computed fields
document.forms[0].fieldName.value="value";
(same with editable fields)

Use regular expression to validate fields.

Some of the stuff that I couldn't get to work:

Cross frameset event-triggering.

Using focus() to jump from a Native OS Field (with focus) to a Notes-field. Most people probably use either one or the other type, so this is probably not a big issue.

Click on buttons/actions using click(). They don't seem to be in the Notes' DOM.

Tell me the rest. I've probably (hopefully) just scratched the surface. :)

Update (02.03.07): Per Kevin's request, I've added a form where you can make per-form configurations. I placed a field on the demo-form with a @DbLookup that gets the configuration. Using eval, I convert the config-field to an object.

The structure of the validations is that of an Object literal. Once you understand how it works, it's really quite simple.

Do a google search on "Object literal" or "JSON" for more information.

I've also added a simple menu in notes, and a simple JS-testing frameset.


v0.001 (old)

v0.002 (new)

Saturday, February 24, 2007

Validation with "Yellow-fade" in the Notes Client

I'm not done yet with JavaScript in the Notes Client.. :D






(The fade is a lot smoother in reality. Low framerate on the flash.)

The yellow-fade can be done on the form you're typing in as well, but that wouldn't look nice. The first field with error gets focus. I'm using setInterval and an array of color-codes to animate the background.

There are advantages and disadvantages using framesets for debugging.
Advantages:
- Always visible (layers are absolutely positioned). If there are many invalid entries, it's easy to forget what was invalid (messagebox)
- You can "animate" the background-color independently of the other frames (FrameName.bgColor)

Disadvantages:
- Always visible (takes up screen estate)

What I don't seem to get around, is using notes-fields for the error messages. This means that you see that they are editable fields. Native OS-fields, which can be borderless doesn't seem to be able to have transparent background(?), and I don't have write-access to computed fields with JavaScript.

I could use a hack to execute Lotus-Script. An editable field hidden by a layer.
Set the value of the field with javascript, "status-header;status message", where status-header in the example above would be error. OnExit ( field.blur() ) on the "LS-field", set value of computed fields (split by ";"). The problem with this is that you have to move the "hiding-layer" every time you add content to the form.


27.02.07 Forget the above. I just tried again. It totally works setting computed field values using javascript.


I'm testing out the possibilities for cross-client (web/notes) for the most common validations. The scripts that gives functionality to the validation would be different according to the client (on the web, using framesets is not an option). What would be common would be the syntax for which fields should be validated, and how they should be validated.

To say which fields should be validated, in the current version, I only need to type this:

var doValidationOn = {
"notEmpty": {"ktype":"Customer Type", "forename":"First Name"}
}

The syntax is:
{
"name of validation" : {
"stored name of field":"Title to show",
"etc":"etcTitle","and so":"on.."
}
}

I think there are smarter solutions. I'm also going to try to add a className-attribute on all fields that's inn an array onload. Although these className's will have no effect on the field in Notes, they can be used on the web for "Really easy field validation", and since every element in the client is an object, I can (probably) use that property to control the notes validation.

var fieldValidation = {
//name of validation : {"stored field":"field title"}
"required" : {"first_name":"First name","last_name": "Last name"},
"date" : {"date_of_birth" : "Birthdate"}
}

By the way, I'm sorry that i have no structure in my posts.. It's all down to lazyness. My plan for this post was just to post the flash, but then my head started butting in.. :[