Question
· Feb 21, 2018

CSP interaction with javascript var

Developers,

I am working with a CSP page with among other fields a checkbox.

Based on the checkbox I want to populate a drop-down box with specific options with a server side method using &html<....>.

I have the status of the checkbox in a javascript var.

Can I access this var or maybe the checkbox setting in the server side method?

Basically how do I know the status of the checkbox to populate the drop-down box correctly?

Any suggestions are appreciated!

Jacques

Discussion (5)2
Log in or sign up to continue

You can just pass any value to the called by #server(..myMethod(arg1,arg2))# or #call(..myMethod(arg1,arg2))# method.

Look at my simple example.

Class User.Page Extends %CSP.Page
{

ClassMethod OnPage() As %Status
{
  &html<<html>
<head>>
  write ..HyperEventHead()
  &html<
</head>
<body>
<label><input id="chbox" type="checkbox" onchange="checkboxChanged(this);">
Choose me
</label><br>
<select id="combo">>
  write ..comboboxOptions()
  &html<</select>>

  &html<
<script language="javascript">

function checkboxChanged(chbox) {
  var options #server(..comboboxOptions(chbox.checked))#;
  document.getElementById("combo").innerHTML options;
}

</script> 
</body>
</html>>
  Quit $$$OK
}

ClassMethod comboboxOptions(chbox = 0)
{
  set options = ""
  if chbox {
    set options = options _ "<option value=""11"">test1</option>"
    set options = options _ "<option value=""12"">test2</option>"
    set options = options _ "<option value=""13"">test3</option>"
    set options = options _ "<option value=""14"">test4</option>"
else {
    set options = options _ "<option value=""11"">value1</option>"
    set options = options _ "<option value=""12"">value2</option>"
    set options = options _ "<option value=""13"">value3</option>"
    set options = options _ "<option value=""14"">value4</option>"
  }
  return options
}

}

Sean,

Thanks for you response. The addEventListener on the click event woks in a 'clean' CSP page I put together and the response is fast.

I did the same with Dmitry's solution and that works as well in a 'clean' CSP page but with a small delay in the response (as you stated - I started clicking more than once as well :) ).

I am actually working with an old style CSP page with a lot of tables in div's that are turned off or on based on various flags and selections from drop down menus and Dmitry's solution does not  work in that setup.

Let me give the addEventListener a try and hopefully this works within the existing solution.

Hi Jacques,

A word of caution.

The server() method requires a little finesse to ensure a good user experience.

What you might not realise is that the server() method bakes a call to the browsers XMLHttpRequest object with a synchronous flag. This will block the main browser process until the response message is returned.

You can simulate a 1 second delay with a hang in the class method...

ClassMethod comboboxOptions(chbox = 0)
{
    hang 1
    set options = ""


When you now click on the check box notice that it does not change its state, not until the response comes back. To the end user this is really bad. They will think it did not work and will click it again. This will normally result in a second request and the checkbox going back to its original state. The user now starts clicking maniacally causing one long UI freeze.

Ideally you want the checkbox to immediately show as checked, to do this you need to debounce the server() call and let the browser do a much needed re-paint. You can do this with a window setTimeout...

function checkboxChanged(chbox) {
    window.setTimeout(function(){
      var options = #server(..comboboxOptions(chbox.checked))#;
      document.getElementById("combo").innerHTML = options;      
    },0)
}


You will now see the check box change immediately.

Thats gives the user some instant feedback, but it wont fully stop them from still clicking on the checkbox. Ideally you want to prevent them from doing it again until the first request has finished.

We can half solve this by disabling the checkbox, like so...

function checkboxChanged(chbox) {
    document.getElementById("chbox").disabled = true;
    window.setTimeout(function(){
      var options = #server(..comboboxOptions(chbox.checked))#;
          document.getElementById("combo").innerHTML = options;
        document.getElementById("chbox").disabled = false;
    },0)
}


However, you will notice that click events are still bubbling up to the re-enabled check box. This is because the event is pending and bubbles up after the check box has been re-enabled, hence why it then gets processed. It means that we also need to debounce this re-enablement to allow the click event to be processed and ignored.

function checkboxChanged(chbox) {
    document.getElementById("chbox").disabled = true;
    window.setTimeout(function(){
      var options = #server(..comboboxOptions(chbox.checked))#;
          document.getElementById("combo").innerHTML = options;
          window.setTimeout(function(){        
            document.getElementById("chbox").disabled = false;
          },0)
    },0)
}

    
This is almost done, the last thing I would do would be to set a fixed width on select box so it doesn't cause a UI re-layout / flicker with different sized contents. And then for good measure have a temp loading message in the first option...

function checkboxChanged(chbox) {
    document.getElementById("combo").innerHTML = "<select id='combo' style='width:150px;'><option>Loading...</option></select>";
    document.getElementById("chbox").disabled = true;
    window.setTimeout(function(){
      var options = #server(..comboboxOptions(chbox.checked))#;
          document.getElementById("combo").innerHTML = options;
          window.setTimeout(function(){        
            document.getElementById("chbox").disabled = false;
          },0)
    },0)
}


There are alternatives that would avoid needing to do this, I will put something together...

Sean.

Staying with the old school CSP approach, I would bake the lists in with the page / page fragment and then swap them in and out using JavaScript. The lists will stay as fresh as the form and the user experience will be very snappy with zero lag, happy users! It will also remember the users selection if they can't make their checking minds up.

Class Foo.SamplePage Extends %CSP.Page
{

ClassMethod OnPage() As %Status [ ServerOnly = 1 ]
{

  set list1="<select id=""list-1""><option value=""A"">Alpha</option><option value=""B"">Bravo</option><option value=""C"">Charlie</option></select>"
  set list2="<select id=""list-2"" style=""display:none;""><option value=""X"">XRay</option><option value=""Y"">Yankie</option><option value=""Z"">Zulu</option></select>"

  &html<

    The Check Box: <input id="cbox" type="checkbox" /><span id="lists">#(list1)##(list2)#</span> 

    <script language="JavaScript">

      document.getElementById("cbox").addEventListener("click", function(el) {
        document.getElementById("list-1").style.display (el.srcElement.checked) 'none' 'inline';
        document.getElementById("list-2").style.display (el.srcElement.checked) 'inline' 'none'; 
      });

   </script>

  >

  Quit $$$OK
}

}