To me, a “required” field means it is required to have some entry in it before you move forward with anything; not that the field is simply highlighted with a colorful border, and you can just nonchalantly ignore the field and move along. However, surprisingly enough, that’s just what Adobe Interactive Forms do within the HCM Processes and Forms “Start Application” for creating and updating forms. But maybe there is some method to that madness…and a simple way for you to insure a required field is truly required. Hopefully, this blog will help shed some light on both issues.
What are the symptoms, doctor?
Here is how this will typically go….
First, as a great HCM P&F developer, you will set your form fields to “required” which requires no coding from you and appears so easy and simple. Of course, you do this because as a good developer, you want to handle as much of the validation on the front end as you can to save any extra trips to the backend. That’s just obvious, right? Next, you will launch your custom process that brings up your form in edit mode. You will not enter anything in your required fields as you are quite secure in the fact that you set these fields in your design as “required” and Adobe will handle the rest for you (thank goodness you didn’t have to write a bunch of validation code yet again, eh?) You simply click the “check and send” button. You will lean back in your comfortable chair all full of pride in your great work. You expect an error message to pop-up at any moment now and tell you to complete all required fields as it stops you dead-in-your-tracks. However, you will actually move quite easily to the next step where you can simply send your form on it’s way. No validation check happened. No pop-up message appeared. No little error even poked it’s head up….not even a peep.
What happened?!?! Surely something must be wrong! Was the form “activated”? Let’s check…yes, it was. Were the form fields actually marked as “required”? Let’s check…yes, they were. What’s going on here? Is ADS down? Is something “flakey” going on? Maybe “they” are running some conversion. Maybe the system is going down. Maybe BASIS is doing something. This can’t be right. What did I miss?!?! What is this bizzaro world I have become trapped in where required fields are not required?!?!?!?! ARRGGGGGG!!!!!!!
It’s just as easy as setting Required Fields in Adobe’s LiveCylce Designer right? WRONG!
First off, what are we talking about here? Well, via the LiveCylce Designer, we have a very straightforward way to set our form fields as “required” for entry. This is a simple matter of selecting this value for our form control as follows from the “Value” sub-tab on our “Object” pallet:
this.mandatory = “error”;
(to turn it off, we could also set it as this.mandatory = “disabled”; )
(** scripting help here: http://help.adobe.com/en_US/livecycle/es/lcdesigner_scripting_reference.pdf)
During runtime, either way of setting this value (static or dynamic) has the effect of highlighting our required fields with a thick colorful border as a visual cue for the end user.
Keep in mind, this functionality is controlled by the end user. They can select to highlight or un-highlight required fields as well as setting the color for the highlighting effect (default is red as shown) all through their Adobe Reader settings and functions. I make a special note of this for two reasons, both due to experience from a recent project. For example, our functional team had written test scripts to distribute out to our global test teams which included the step “verify required fields appear with a red border”. This would be failed by some testers because (1) they had not selected the “highlight required fields” option in their Reader (2) they had changed the color of the highlight function or had turned it off completely. Lessons learned. (haha) In addition, we encountered a VERY strange behavior in which the fields would highlight for one user but not for another. Stranger still, if testing using two different test users on the same machine via the portal, fields would highlight for one test user but not the other even through the same process! This is still an ongoing issue to be resolved. My feeling is that it might have something to do with users having both Adobe Reader and Acrobat in different version on the same machine or various Reader versions installed or they simply need to upgrade to Reader 9.0 (currently most are on 8.1.4). But I digress. Back to making our required fields required!
Now, back to our settings, we see many options when we mark the field as “required”:
As you see, we have all kinds of great validations available. First off, we can display a message if the field is left empty. Also, we could set a validation pattern and then have a “validation pattern message” appear if this pattern was not correctly entered. Lastly, we could set a validation script to run and also display a message. All this is well documented in the LiveCylce Designer help documentation. Sadly, for us, it is pretty much of little to no use.
Why it does not work as you might expect
To understand why our required fields do not seem to work as required fields, we really have to understand what is really going on under the hood with our Adobe Interactive Form. Basically, it is quite simple. Keep in mind that our actual “form” is merely a template or window through which we are entering data. The data is the important part and it is simply saved behind the scenes as XML. For the most part, our form is not truly submitted but saved. This is the key! If we review Adobe’s help documentation in regards to form field validation, we will see the most telling of clues:
In case you can not read the small print there, it reads:
Note: Users can save and close a PDF form without providing required values. In this case, no validation is performed.
Forcing a square peg in a round hole just does not work
Given this information, if you are like me, you might take this as some kind of challenge. I thought “ok…well, I will just force the validation another way. I’ll trick it into working!” Not so fast there tough guy. Haha I placed script code into just about every other event to in turn fire my validation check for the required field. For example, maybe during the “change” event of one control, you check to make sure it adheres to some business logic. Sure…..it fires…but only if the “change” event happens. That’s not always a given. In other attempts, it would fire the validation, but too much (ie. constant pop-up windows) or in the wrong sequence. In the end, it just was not consistent or easy to implement.
And here is the most important reason of all why it just does not work…..No matter what you do to trigger the validation check, even if an error occurs, it does not stop the transition of the application from going from “check and send” to “send”. The user still moves along in the process! The only thing that will stop the form from moving forward and truly make the user correct their entries is a “hard” error that comes form the backend. Therefore, our solution is quite simple.
Let’s Try a Different Approach….How about setting it in the backend? Nope.
If you look closley at the form field configuration under your form scenario in the backend, you will see a setting for “field attribute” which according to the documentation, “determines the field properties for each field that you use in an interactive form”. This “should” affect how the form field behaves on the front-end by the setting in back-end. However, up till now, this setting really is of little to no use (ie. I have never seen it’s effect actually work!) I have gotten into the habit of just not using it (as I want to set this how I want on the presentation layer, ie. form, and not from the backend dictating it). If anything, it can be used to “document” the usage on the form. There is OSS note 1239961 which does fix this a bit. It makes it so that if you set a form field to “required”, it will actually check that the field is not empty (ie. mandatory), but it does not do any other real validation for us. So, this one too is really not our option. Next?
If you build it, they will come….or at least your form will act right
First off, if you have in fact bound your form fields (via config) to standard backend services (SAP_PA, SAP_PT or SAP_PD), you will receive any and all field validations that they handle. However, in many cases, the messages you receive will be a little cryptic and not very telling to the actual cause (ie. “field so-and-so is empty”). In most cases regardless if our fields are bound to standard services or not, we will need to provide our own custom field validations. How do we do this? In the end, we will be back to our good ol’ friend, the generic service.
Personally, I have yet to develop a form scenario/step that required some level of form editing (create/update) that did not require a custom generic service for validation. However, the approach to this might vary.
I will not go through what a generic service is and/or how it works, but I will give you a rough idea of a few approaches I have seen/done.
First off….the “utility” approach. You can develop a “common” validation service that contains a number of “generic” operations. This could be re-used across a number of processes/scenarios/steps. For example, your operations might include “IS_EMPTY”, “IS_NUMERIC”, “IS_NEGATIVE”, “IS_VALID_DATE”, and on and on and on. In this way, via your configuration, you would call very specific operations. The drawback here is that you will have several operation calls mapped to several field groups. Also, the error messages you return would be fairly generic.
Second…the simple form step specific approach. Using this approach, you would write a validation generic service very specific to your particular form scenario step. You would pass over all the fields as one field group to the generic service. Within the service, you would collect the field values and perform your required validations. The advantage here is that you can customize the error messages very specific to your form (ex. “Please complete the new salary amount below current salary.”) The disadvantage here is that you might be using other validation services for other steps within the same process and the validations can easily become disjointed or inconsistent. You might also be repeating code in services that could otherwise be shared/reusable/modularized.
Third…the process or form scenario approach. This approach is a bit more involved; however, it will keep all validations related to a process encapsulated within one service. The key here is that within your field groups that you will pass to operations, you will be sure to pass the FORM_SCENARIO_STAGE field. This field contains the exact name of the current form scenario step. Therefore, within your service, you could use a “CASE statement” to check this value and determine specific validation operations based on that step. For example, perhaps in the “request” step we want to validate some set of fields, but then in the “approve” step, we want to check those fields plus an additional set of fields. This is quite easy to code for using the FORM_SCENARIO_STAGE field. This service will be very specific to our proecess/form scenario, so keep that in mind should the process change.
Beware, due to how the HCM P&F “dispatcher” collects information and processes our services, you need to make sure that your form scenarios call different operations (ie. the names) although these operations might trigger the same internal private methods. This is because it will attempt to collect all datasets used for an operation and call it. So if a previous set of data calls the same operation name, it will try to use this. I have personally seen this happen when a “return to author” action sent “step 2” back to “step 1”. It would actually call the service with the date from “step 1” but would call it again using data from “step 2” which would error for us due to our validations. It should have only used the data from the step we were on (step1). This was because step 1 and step2 used the same operation name, but we decided what to do based on the step name internally. Again, simply changing the operation name (although it called the same method in our generic service) for step 2 fixed this.
BONUS: Trick from the trenches
If you have read through everything this far (God bless ya!), I will give away one of my little tricks for how I handle validation services….free of charge for once! (haha) For all of my validation services, I include an additional field in my form fields and my service fields that is a simple “trigger”. In form field configuration, it is a simple one character field. Let’s call it “FLAG_VALIDATE”. I then place this field on my form and set it’s “form ready” event to set it’s value to “X” (this.rawValue = “X”;). In the backend, I have this field mapped to my generic service (the field in the service, I typically call “EVENT_TRIGGER”) to my validation operation. Let’s say “VALIDATE_FORM”. So my mapping might be:
form field FLAG_VALIDATE -> field grp VALID -> operation VALIDATE_FORM -> service field EVENT_TRIGGER
Next, I create a rule that I attach to the VALIDATE_FORM operation. It will be something like “FLAG_VALIDATE = X”. Therefore, my validation service only gets called when the FLAG_VALIDATE field is equal to “X”. Now, back over to my form, for ANY other form field (date picker, text input, button, etc) that triggers any other kind of backend user event where I do NOT want the validation service to trigger (because a user might still be filling in information), I will modify the event associated with the control that calls my user event to also set the FLAG_VALIDATE raw value to “” (blank) before it calls the user event. In this way, we are “tricking” our validation service not to be called, but as the form returns back to us, the validate flag will be reset to “X” in the “form ready” event. Pretty sneaky eh? (haha)
So that’s it. That’s “why” the Adobe form required fields do not quite act as we might expect, and also how we whip them back into shape to get our intended results. Now, you can get back to leaning back in that comfortable chair and taking pride in your clever work. Till next time….