Search
Categories
Tags
Latest Comments

Entries in validation (2)

Wednesday
May052010

Conditional validation - making validation more developer friendly

Validation in XPages is sometimes extremely confusing to work with. Especially when you have partial updates on the page.

If you want to trigger a partial update on a page with validation, you need to either set the event that updates the page to Set partial execution mode (ignores validation), or Do not validate or update data (ignore validation/don't calculate fields).

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've written a function that lets you test if a specific component triggered an update.

function submittedBy( componentId ){
try {
  var component = getComponent( componentId );
 
   if( !component ){
    throw new java.lang.Exception( 'Component with id "' + componentId + '" not found' );   
   }
 
   // Cache the id of the event handler
   var eventHandlerId = viewScope.get( 'ev-' + getClientId( componentId ) );
 
   if( !eventHandlerId ){
    // Find the component's event handler
    var eventHandler, childNodes = component.getChildren();

    for( var i = 0; i < childNodes.length; i++ ){  
      if( typeof childNodes[i] === 'com.ibm.xsp.component.xp.XspEventHandler' ){    
        eventHandler = childNodes[i];
        break;
     }
    }
    eventHandlerId = eventHandler.getClientId( facesContext );
    viewScope.put( 'ev-' + getClientId( componentId ), eventHandlerId );
   }
 
   // Compare eventHandlerId with $$xspsubmitid
   var submitId = param.$$xspsubmitid;
 
   return ( eventHandlerId === submitId );
  } catch(e){ /* your exception handling */}
}

Add this function to a SSJS script library.

If you only want the validation to run when the user clicks a specific button, write this in the required-attributes: return submittedBy( 'id-of-save-button' )

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; }

id-of-save-button is the id/name of the component that triggers a save.

The above code examples makes only triggers validation when a component with id id-of-save-button triggers an update.

If you want multiple actions to trigger the validation, you can do something like this:

if( submittedBy( 'id-of-save-button' ) || submittedBy( 'id-of-save-and-close-button' ) ){..}

When I initially wrote about this technique in my blog, I said that it was a downside that you had to add code to the validators to make them execute conditionally. Since then, I've started using this technique in all my production code. Both my colleagues and me agree that forms in XPages are a lot more predictable when using conditional validation. Less time debugging equals more RAD. :)

Share and enjoy!

Wednesday
Jul222009

Validation and No Data Validation all in the one XPage.

Probably the best thing about being the XPages test lead for IBM Domino 8.5x is that you get to play, sorry I mean work with the thing all day. Seeing all the cool stuff that can be done with XPages is also great and you don't have to be a super-duper developer (or a developer for that matter) to get going. If it didn't take you long to design databases on Lotus Notes, then it's not a big step to XPages.

Part of my role as test lead is to monitor the forums and blogs. Pick up on how people are using XPages and if they hit problems, get them addressed. These problems usually fall into two categories, Bugs and How-to's. The validated bugs get put into the system and the team here resolves them. The How-to's a more fun. Here you get to chance to try ways, push the technology and find solutions.

One of the first How-to's which I worked on after the release Lotus Notes Domino 8.5 was in a nutshell to do with performing validation and no validation all on the same XPage. The scenarios was this. You have an editbox that has Ajax type-ahead going on for a person's name - with the suggestions populated by a @DbColumn lookup. If the desired name exists in the list, upon leaving the editbox a @DbLookup is performed using the person's name as a key which gets the details of that record and populates other fields further down in the XPage. And if the name doesn't exist or you have a new name, the person's detail fields need to be filled-in (or filled-out) manually. And of course, some of these details are required and some are not (see snippet).

<xp:label value="Full name:" id="FullName_Label1" for="FullName1"></xp:label>

<xp:inputText value="#{document1.FullName}" id="FullName1">

<xp:typeAhead mode="partial" minChars="1" ignoreCase="true">

<xp:this.valueList><![CDATA[#{javascript:@DbColumn(@DbName(),"vwProfiles",1)}]]></xp:this.valueList>

</xp:typeAhead>

<xp:eventHandler event="onblur" submit="true"refreshMode="complete">

<xp:this.action><![CDATA[#{javascript:var nm = @DbLookup(@DbName(), "vwProfiles", getComponent("FullName1").getValue(), "FullName");

Company = @DbLookup(@DbName(), "vwProfiles", nm, 2);

Email = @DbLookup(@DbName(), "vwProfiles", nm, 10);

if(nm == null){

getComponent("Company1").setValue("");

getComponent("Email1").setValue("");

}else{

getComponent("Company1").setValue(Company);

getComponent("Email1").setValue(Email);

}

}]]></xp:this.action>

</xp:eventHandler></xp:inputText><xp:br></xp:br>

<xp:label value="Company:" id="Company_Label1" for="Company1"></xp:label>

<xp:inputText value="#{document1.Company}" id="Company1"required="true"></xp:inputText>

<xp:message id="message1" for="Company1"></xp:message><xp:br></xp:br>

<xp:label value="Email:" id="Email_Label1" for="Email1"></xp:label>

<xp:inputText value="#{document1.Email}" id="Email1"required="true"></xp:inputText>

<xp:message id="message2" for="Email1"></xp:message><xp:br></xp:br>

<xp:button value="Submit" id="button1"><xp:eventHandler event="onclick" submit="true"

refreshMode="complete" immediate="false" save="true"></xp:eventHandler></xp:button><xp:br></xp:br> 

This is where the fun starts.

When we run this, it fails on the onblur event as validation kicks in before the details from the lookup can be set to the Company and Email fields.

So, lets try setting no data validation on the onblur event. That should take care of things, right? Wrong. This acts like a cancel action and thus stopping the setting of the Company and Email details. Back to square one.

There's got to be a way, and there usually is with XPages. The answer lies in knowing a tiny bit about the JSF lifecycle. Just a tiny bit now - don't think you got to start reading books upon books about it, and thankfully my colleague Maire Kehoe has written an article titled "The Events "No data validation" option in XPages" in the Designer Wiki 

What we are going to have to do in our example is catch the values before they hit validation. And to do this we are going to use 'getSubmittedValue()' instead of 'getValue()' for the lookup, and then use 'setSubmittedValue()' instead of 'setValue()' to add the details to the Company and Email fields.

var nm = @DbLookup(@DbName(), "vwProfiles", getComponent("FullName1").getSubmittedValue(), "FullName");

Company = @DbLookup(@DbName(), "vwProfiles", nm, 2);

Email = @DbLookup(@DbName(), "vwProfiles", nm, 10);

if(nm == null){

getComponent("Company1").setSubmittedValue("");

getComponent("Email1").setSubmittedValue("");

}else{

getComponent("Company1").setSubmittedValue(Company);

getComponent("Email1").setSubmittedValue(Email);

}

Now the onblur event can do it's job, and validation also happens when the required fields haven't been filled in.

Hope this helps? Now back to work... sorry I mean play with XPages.

p.