RichInputListOfValues - accept unlisted values and update model

JDev: 12.2.1.3.0
Source: GitHub

Business requirements can be strange and demanding at times. They would make you look deeper into the framework and try to find out mechanisms to bypass usual behaviour.

I recently worked on a requirement where the customer was in love with Input-List-Of-Values component and wanted to know if I could stretch it further. Here's what he wanted:

1. Allow the Input-List-Of-Values to accept values which were not in the list.
2. On entering such a value, fire a validation which would show a warning message that the value is not in the list.
3. From the developer's stand-point, we also wanted to update the model then and there, as we were using the current row's attributes for some other logic processing. Of course, this third point was our concern, not the customer's.

So let's see how to go ahead with this. I have an application which displays one row of a transient view object. The "FirstNameAttr" attribute of this view has one LOV from Employees table in HR schema.

The initial input-list-of-values component looks like this:

Original component

To allow the component to accept unlisted values, we remove the model validator. This takes care of requirement 1.

Requirement 1 solved - removed validator tag

To solve requirement 2 - fire validation on entering a value - we cannot use autoSubmit and valueChangeListener. That would only cause the framework to launch the LOV popup window and ask you to enter a value from the list. So let's try with a client framework. We add a clientListener to fire on valueChange, and an associated serverListener to fire a method on my AMImpl. Based on the output of this method, I display a message on the screen.

Requirement 2 solved - added clientListener on valueChange

The output

Now that should have been the end of it, but from our end, we also wanted to update the model. So I asked the model to be forcefully updated.

clientEvent.getComponent().processUpdates(JSFUtils.getFacesContext());




But when I printed the row with all its attributes, I saw that the FirstNameAttr is still null on the row!


Now why is that? That is something which works for Value-Change-Listener, doesn't it? Well it does not work because it should not work!

Client-Listeners are executed in the APPLY_REQUEST_VALUES phase of the JSF life-cycle. Before we can update the model, the PROCESS_VALIDATIONS phase has to get fired, and the UIComponent tree has be built and submitted for model update. Unless the validations are completed, the model cannot be updated.
The reason it works for a Value-Change-Event is that a VCE gets fired at the end of the PROCESS_VALIDATIONS phase, and any model update which is programmatically done is happily carried forward to the UPDATE_MODEL_VALUES stage.

So what do I need to do in order to ensure that the UI is validated and submitted for model update in the APPLY_REQUEST_VALUES stage of the life-cycle? The answer is - I make the input-list-of-values as immediate="true". This bumps up the priority of this component and it gets validated in the APPLY_REQUEST_VALUES stage, and my wish for model update is granted.

Remember - setting immediate property for an input component does not give it any super-powers. It merely causes the component to be validated earlier than usual, which, in my case, solved the problem.

Now when I print the row, I get what I want.


Another point worth mentioning is - if I print the same row through a button ActionListener, I see the same result as above (even without immediate on list component). This is expected because, the button listener is fired in the INVOKE_APPLICATION phase, and by that time the UI has been validated, and the model has been updated.

It is important to note that the LOV component will not fire its client listeners when we select a value from the popup window. This is because, all popup related events are only fired in the INVOKE_APPLICATION phase. To get past this, I have added a returnPopupListener, which fires the validation.

So the final UI component is this:



Cheers!

Comments