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

conmic
Mega Guru

Hello David,



nice solution to your problem, this may help some people.



I don't want to be a buzz-killer, however after reading the following part


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.


I asked myself if the "readonly_clickthrough" attribute for dictionary entries would have worked?


It's specifically for reference fields to leave the "i" available when read-only.


Capture.PNG



Kind regards,


Michel


I needed the "i" clickthrough to appear next to the string Caller field, not the hidden reference Caller field.   The only way to get this done was to code what I did.   Would you agree?


This is a "String" field:



find_real_file.png






This is a copy of the Caller reference field (that's hidden)



find_real_file.png


Hello David,



I read through it again and indeed I missed that part. sorry



I wonder though:



1. Why would you want to make the reference field mandatory on the form if it's read-only?


It's kind of contradictory and I think that's also why the system was designed like that.



2. So is it that you enforce that the field is populated with a value?


In that case, the best practice is to use a business rule that enforces this behavior


"Mandatory" fields are kind of a UI, client-sided thing. This can be pretty unreliable. And ServiceNow does also not recommend to enforce data-policies via client sided mechanisms.


Business rules on the other hand run on the server side and can ensure that data is correct.



I really don't mean to be a pain... It's just something one should be considerate about in my opinion.


Again, your solution is pretty proper and may help in some other cases! So I really compliment your work.



Kind regards,


Michel Conter