Read-Only and Mandatory workaround

xiaix
Tera Guru

Became victim of the famous Read-Only & Mandatory field problem.   But... I found a workaround (albeit a hack) and am curious if this is the best solution.   I'm worried about future compatibility with instance upgrades.

Here's the deal...

We have an HR form that has an "Employee ID" field, and a "Caller" (sys_user reference) field.   HR does not want to enter the name of the employee into the Caller field, but rather enter their Employee ID, upon which will auto-fill in the Caller field.

So, we needed the "Caller" field to be mandatory but read-only also.

The solution was to put a String field (not reference) on the form and make THAT read-only, not mandatory.   We then made the reference Caller field mandatory, and although we couldn't make it a read-only, we simply hid it via element.style.display = "none" in an onLoad client script.

The frustrating part was HR also wanted the "i" hover button to appear next to the Caller field so they could hover over it to get more Employee info.   Well that was a head-scratcher, so here's what we did.

Allow me to explain all steps:

First, the onChange client script that makes the User ID field auto fill in the hidden Caller field:

find_real_file.png

function onChange(control, oldValue, newValue, isLoading)

{

      if (isLoading)

              return;

      if (newValue == '')

      {

              g_form.setValue('u_caller', '');

              g_form.setValue('u_caller_display', '');

              return;

      }

      if (!g_form.getControl('u_caller'))

              return;

      var gr = new GlideRecord('sys_user');

      gr.addQuery('employee_number', g_form.getValue('u_userid'));

      gr.query();

      if (gr.next())

      {

              g_form.setValue('u_caller', gr.sys_id);

              g_form.setValue('u_caller_display', gr.name);

      }

      else

      {

              g_form.setValue('u_caller', '');

              g_form.setValue('u_caller_display', '');

              g_form.showErrorBox('u_userid', 'Invalid UserID');

      }    

}

Now, the onLoad client script that hides the reference Caller field, and copies the "i" info hover button up to the string Caller field and places it perfectly:

find_real_file.png

function onLoad()

{

      var outer = '';

      // First, capture the "i" reference icon code and stuff it into the "outer" variable

      var info = gel('view.u_human_resources.u_caller');

      if (info)

              outer = info.outerHTML;

      // Hide the real reference field that's mandatory, but not read-only

      var a = gel('element.u_human_resources.u_caller');

      if (a)

              a.style.display = "none";

     

      // Create the "i" hover button shell.   (it's just an <a> tag)

      var caller_info_hover_button = document.createElement('a');

      if (outer != '')

      {

              // Create an object of the String version of the Caller.

              // This should be a <div>, which contains other divs, the last one being the placeholder

              // for the "i" button icons to hover over.

              var u_caller_display_FIELD = gel('element.u_human_resources.u_caller_display');

             

              // Get the last <div> of the string Caller element

              var lastChild = u_caller_display_FIELD.lastElementChild;

             

              if (lastChild)

              {

                      // Append our "i" hover button shell we created above.

                      lastChild.appendChild(caller_info_hover_button);

                     

                      // Take all that copied info we did at the start and stuff it into this shell

                      caller_info_hover_button.outerHTML = outer;

              }

      }

}

End Result:

find_real_file.png

find_real_file.png

find_real_file.png

18 REPLIES 18

Your point #2 is the correct assumption... to enforce that a Caller was populated.



You're not being a pain at all... I'm encouraged by honest technical feedback that's well thought out, written and explained, such as yours.



I agree too this could have either been handled as an "onSubmit" UI Policy, or as you suggested a Business Rule.   I however wanted the checking to take place as dynamically as possible without the need to validate on Submit.


thanks for the understanding.



I understand your point, however consider to back your code up with an onBefore business rule... just in case.


I think that's a good idea too.   It's never safe to solely depend on client-side code for validation.   A "backup" business rule is prudent in this case.


oliverschmitt
ServiceNow Employee
ServiceNow Employee

Just btw: Using GlideRecord and gel in Client Scripts will pop up in all ACE reports and is considered bad practice.



http://wiki.servicenow.com/index.php?title=Client_Script_Best_Practices#gsc.tab=0


Agreed as I could have (should have) used a GlideAjax call to get the value.   I'll most likely change it to this.   Thanks for bringing up a valid concern.