The theme of my session at IBM Connect (which will be repeated at the end of this month at Engage) focussed a great deal on the XPages lifecycle and how to troubleshoot and understand it. Once you do have that level of understanding, there are some powerful things you can do with it. Here is an idea of one.

I had a form where I needed validation under certain circumstances. One of the key things to bear in mind is that you must have a required validator if you want any other kind of validation. The first scenario where I needed validation was for new documents. The way less experience developers might approach this (and I stress there is nothing wrong with this approach) would be to compute the required property and compute the loaded property of the validateRequired validator. But I’ve moved to using a page controller and trying to minimise the SSJS on an XPage and particularly minimise the multiplicity of similar computations on the XPage. So I looked to programmatically add the validator.

There were a few bits of knowledge that I leveraged, pulled from various sources. The first was from Vaadin and the Local folder of an XPages application in Package Explorer. The link in both is that Java is used to generate the UI. So there are programmatic methods used to set properties and add things like validators. The second was the binding property on a component, which Tim Tripcony did a NotesIn9 about and which allows you to bind an actual component to a property on a Java object, whether that be a custom validator in Tim’s case, a managed bean or a page controller. Combining the two means my page controller can programmatically call the methods to set required to true and add a validator with the following code:

  1. if (getMainDoc().isNewNote()) {
  2.     getPassword().setRequired(true);
  3.     RequiredValidator pwReq = new RequiredValidator();
  4.     pwReq.setMessage(“Please enter a password”);
  5.     getPassword().addValidator(pwReq);
  6.     getPasswordCheck().setRequired(true);
  7.     RequiredValidator pwCheckReq = new RequiredValidator();
  8.     pwCheckReq.setMessage(“Please confirm password”);
  9.     getPasswordCheck().addValidator(pwCheckReq);
  10. }

Line 1 checks whether the dominoDocument datasource is a new document or not. The password and passwordCheck properties of my controller (accessed via the relevant getters) are bound to components on the XPage. The very important point to remember, stressed by Tim in the NotesIn9, is that the UIComponent classes are not serializable, so any component specified as a property in a Java class needs to be set as transient, so private transient UIInputText password. After setting required to true on the component, I add new com.ibm.xsp.validator.RequiredValidator objects with the relevant error message.

One of the other aspects of my session was the importance of knowing the lifecycle and being able to troubleshoot, but even then it’s possible to be too clever and confuse youself! When I added the code to the beforePageLoad event, I got the following stack trace:

stack trace

I’ve highlighted the relevant part, which is a NullPointerException encountered during the createView method. This is when the component tree is being loaded. That confused me more than a little, because it implied a problem with one of the components when it was being added to the component tree. However, after running through with the Java debugger, the error occurred in my beforePageLoad in line 2 of the code snippet. Again I was confused, because the binding seemed correct and, after re-watching Tim’s NotesIn9, I still couldn’t see anything wrong. The root of the problem is that the beforePageLoad event runs before loading the component tree, so at that point the code was running and throwing an NullPointerException, the components had not yet been created. Because I’ve always used that method, it was my natural instinct to use it again. Moving it to the afterPageLoad event instead solved the problem.

What is also worth mentioning here is that my persistence settings is the default of “Keep only current page in memory”. I don’t think it will affect things, because although the binding to the component is transient, so the binding is dumped when it’s serialised, the code that adds the validator is not applied to the Java property, but to the underlying component. So removing the binding on the property will not remove the validator. That means adding it in afterPageLoad is fine, it doesn’t need adding after every partial refresh in the render response.

The extension to all this is the scenario where the validation needs to be triggered if the document is not new, but a new password is added. Again, there are a few ways of handling all this. One would be to hide the components if it’s not a new document and handle changing passwords with a button that’s only available in read mode and launches a dialog. This is a perfectly valid and possibly preferable UX option. Another option would be to add the validation on the save() method, aagin a valid approach.

Otherwise, validation can conditionally be triggered on the components. It’s critical to remember that any validator will only trigger if the required property is set to true. But the password and password check fields shouldn’t be required – we don’t want the password to always be entered, we just want to ensure the values are the same if a new password is added. This is similar to the scenario I had in my session at IBM Connect where I only showed the save button if the form was valid, but of course needed to hide the save button if the form was made invalid. In this case, we know the password check field is required if a value is entered in the password field. The first element here is to trigger a partial refresh to add the validation if a value is entered in the password field. But we also need to remove the validation if a value was entered but is then removed. In the onblur event of the password field, it just needs to add the following code:

  1. public void addValidator() {
  2.     if (!“”.equals(getPassword().getValue())) {
  3.         getPasswordCheck().setRequired(true);
  4.         RequiredValidator pwCheckReq = new RequiredValidator();
  5.         pwCheckReq.setMessage(“Please confirm password”);
  6.         getPasswordCheck().addValidator(pwCheckReq);
  7.     } else {
  8.         getPassword().setRequired(false);
  9.     }
  10. }

The code sets the required property and adds a validator if the value isn’t blank. It also removes the required property if the value is blank (which will also disabled the RequiredValidator). The eventHandler can be enabled only if this is an existing document either by setting the loaded property or using CSJS to set return false if not a new document.

What is worth stressing is that I would not be doing this for performance reasons. The performance benefits would be negligible compared to loading resources, lifecycle processing code and impact of network latency. Performance is also about the amount of time it takes to develop and test, but also about whether learning is likely to be reused. These aspects are often not considered when the topic of “performance” is raised, but it’s one I consider very important. For me this is about trying to keep my XPage as clean as possible and pushing as much through the page controller as possible. It’s also about understanding the page lifecycle better and better. And I also mentioned Vaadin, and it’s an approach that might be utilised for Vaadin to manage validation when entering a page based on what the current document is.

3 thoughts on “Using Bindings for Dynamic Validation”

  1. Pingback: SSJS Avoidance: Inspect Component Properties using Component Binding – Cameron Gregor

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.