Wednesday, December 30, 2009

Conditionally Required Fields in Oracle ADF Faces Rich Client

It happens to have functional requirement, when specific input field becomes required based on another field value. Sounds clear, however there is one trick you should know while implementing this requirement. First thing ADF developer will try, he will define AutoSubmit and PartialTriggers properties between some field and dependent conditionally required field. What happens during runtime, it is not working so straightforward. If we define PhoneNumber as dependent attribute from JobId, and implement attribute required condition to be true when JobId = AD_PRES:



We will face problem in a case when PhoneNumber field will be empty. User will try to change JobId field value to something else, but he will be stuck because framework will report required field validation error:



This happens because there was defined PartialTrigger dependency between PhoneNumber and JobId. Any change in parent attribute triggers child attribute, where validation is invoked and failed for empty value.

Instead of defining PartialTrigger dependency directly between two fields, we should invoke it through partial trigger dependency in Backing Bean code. Let's define ValueChangeListener for parent field, JobId in my case:



Listener code is self explanatory, based on JobId value, we are setting PhoneNumber as required or not and then invoking partial target. Its a key difference comparing to PartialTrigger dependency, now we are changing dependent attribute required property first and only then triggering this attribute refresh. This allows to avoid validation error while changing condition for required property:



It works fine, but you shouldn't forget initial PhoneNumber correct rendering, there is already predefined expression for required option. This creates trouble, because we are trying to change it from value change listener, ADF_FACES-60058 exception is thrown while changing JobId:



Its because exists already predefined expression language for Required property:



Now we will apply old trick from ADF 10g, instead of using expression language, will calculate Required property value in Backing Bean method:



This method works in the same way as value change listener explained above, only one difference - it always returns false:



Main trick - you should define this method not directly on Required property itself, but on unrelated property, let's say - Readonly. This allows to avoid ADF_FACES-60058 described above. Its why we are returning always false, because field should be always editable.

On runtime, when JobId is not AD_PRES, PhoneNumber is not required:



If we navigate to such record, where JobId is AD_PRES, PhoneNumber will be automatically required:



Find employee with JobId not equal AD_PRES and change it to AD_PRES:



PhoneNumber field will become required:



Now try to change JobId again, you should be allowed to do the change:



PhoneNumber field will become not required:



Download sample application - ConditionalMandatory.zip.

10 comments:

  1. Hi,

    Thank you very much for this blog.
    I have a query region method isPhoneNumberRequired()? How this #{backingBeanScope.backing_main.phoneNumberRequired} would call isPhoneNumberRequired() method as method name looks different?

    Also Another query which is not relaed with this. Could you please tell me what is difference between managed bean and backing bean?

    ReplyDelete
  2. Hi Nandy,

    Backing bean is Oracle term for Managed Bean in Request scope.

    isPhoneNumberRequired() is Bean method that return boolean type, by JSF standard through expression language this method is called as #{beanName.phoneNumberRequired()}

    Regards,
    Andrejus

    ReplyDelete
  3. Hi,

    Is nice to understand this major difference between 'partialTriggers' and 'addPartialTarget' functionality.
    From official Oracle documentation this difference wasn't that clear.

    Thanks Andrejus!

    ReplyDelete
  4. Hello Andrejus,
    nice post and good practice,

    but I have a question why did you create your bean on backBeanScope ?and what different between backBeanScope and requestScope?

    Regards
    Karim

    ReplyDelete
  5. Andrejus,

    How can change required attributte ina field content in Table.

    Thanks

    ReplyDelete
  6. Hi,

    It should be the same as in form component. Input field contains Required property as well. Or you are facing specific problem?

    Regards,
    Andrejus

    ReplyDelete
  7. Hi Andrejus,
    I have a list of tasks in a page which are edit in a form in a ragion. the form has an LOV for status representation of task.
    If the status of the task is selected as duplicate then a field basetask has to be made mandatory..
    I'm able to do this for the first edit.
    When i save the first and open edit another task for edit the requiredness of the basetask field is same the previously edited task . It dont reset it based on the my new status value of this task.
    When i debug the backing bean method for the required field is executed only once for the fist edit after loading the task page. from the second edit, the next time when the region is invoked it doesnt execute the method. (here isphonenumberrequired in ur case.)

    Can you suggest any solution.
    Thanks

    ReplyDelete
  8. Thank you, it's really useful blog.

    ReplyDelete
  9. Thanks Andrejus for the nice blog..

    ReplyDelete