getExternalValue, postMessage and controlled vocabularies

Sometimes you want the user to be able to choose from many hundreds of options. And you even have these in a database, sometimes called a controlled library, but you definitely do not want to type in a enormous amount of response-options and values. And this library may change over time and changing your CRF to synch with this is not an option.
You can solve this by using the function getExternalValue in your CRF.

compatibility

Be aware that this only works in Firefox. Or putting it more precisely: I could not make it work it in IE (8 or 9). As reported by Can I use the postMessage is partially supported, but not across two windows. Sorry. (See also this page.)

the easy part: your CRF

Setting up an item to accept an external value is (very) easy. In our example we will make an item of RESPONSE_TYPE calculation. We can then define where to get the external value. A link will be displayed and clicking it will open a browser window. There you can choose a value and this is returned to the OpenClinica-form and inserted in the item.
In fig. 1 you can see a one-item-CRF. This item has both a LEFT_ITEM_TEXT and a RIGHT_ITEM_TEXT.
Then column RESPONSE_TYPE (N) must be calculation. Because we will use column RESPONSE_VALUES_OR_CALCULATIONS (Q), we must supply something for RESPONSE_LABEL (O) and RESPONSE_OPTIONS_TEXT (P). In fact it does not matter what you fill in here. Of course, if you have more than one item with getExternalValue, then you must give each a unique RESPONSE_LABEL.


fig. 1: The CRF columns

Now we come to the function.


fig. 2: The function getExternalValue

The function in column RESPONSE_VALUES_OR_CALCULATIONS is
func: getExternalValue("http://www.trialdatasolutions.com/tds/howto/pm.htm",right,500,300). The components are more or less obvious:
first the URL where the page with the controlled library is, then left or right, to choose if you want the left- or right-item-text to be the link, and then the height and width of the browser window.

Upload your CRF and attach it to an EventDefinition and it should look something like fig. 3.


fig. 3: The CRF

Clicking on the link will open a new browser window, as defined in your spreadsheet. Notice that it has a reference to the item on the OC-form in the querystring of the URL: item=mainForm.input4345.


fig. 4: The new browser-window with the Controlled Library

but now the other part

The preceeding part is not so difficult, the part to come is a bit trickier.

First of all: what do we need? According to the documentation, we must use the function postMessage. I will first display the source of the page with the JavaScript to do this and then explain how it works.

<html>
<head>
<script type="text/javascript">    
function postItToOC () {            
    // start by taking the query string and from that the part after
    // item=
    // and that should be something like mainForm.input4343
    var mes=window.location.search;
    mes=mes.substring(mes.indexOf("item=")+5);
    // now read the choice of the select
    var select = document.getElementById ("mySelect");
    // and concatenate the value with mes
    mes=mes + ":" + select.value;
    // mes is now something like
    // mainForm.input4343:2
    window.opener.postMessage (mes, "*");        
}
</script>
</head>
<body>
<p>This is an example of the method to use if you want to insert a value from another page/URL into an item of an OpenClinica-form</p>
    <select id="mySelect">
        <option value="1">First option</option>
        <option value="2">Second option</option>
        <option value="3">Third option</option>
    </select>
    <button onclick="postItToOC ();">Post it to OC</button>
</body>
</html>

try it

Before we explain this: try it. You can make a CRF and use the exact same link to this page, http://www.trialdatasolutions.com/tds/howto/pm.htm. Experiment with your CRF etc. Then download the page to your own environment and use it as a startingpoint for your own page. There are many ways of developing jscript, but I would recommend testing in FireFox with the FireBug-add-on.

how does it work?

When the OC-form is loaded into the browser, it creates a listener, that listens to messages. This is all taken care of: you do not have to do anything extra.
The OC-form is displayed and the user clicks the link and the Controlled-Library-form (CL-form) is opened. On it is the option to select something, plus a button. When the user clicks this button, a function postItToOC is called. This function sends a message to the browser with the OC-form. The listener of the OC-form receives it, checks it, extracts the content for the CRF-item and writes that to the form.

Let's get to the details. We start with the last step on the CL-form: the sending of the message to the OC-form. In the script this is done on the line window.opener.postMessage (mes, "*");. mes is the message to send back to the OC-form. postMessage is the action of sending the message, but the script must know who to send the message to. And the answer is: "send it to the browser window that opened this window", which is defined as window.opener, resulting in the OC-form, because in this context the window is the CL-form.
The "*" means we allow the message to be sent to any domain. This is of course a security risk and in most cases you would put here the domain of your company or institute.

So we know how to send the message, but what should the message look like? Of course we want to send the chosen option and this is done with select = document.getElementById ("mySelect") and select.value. But we can not send this as the content of our message! The format is not so very well documented, but it must be something like mainForm.input4345:2. Look at the line in showItemInput.jsp that goes
if (e.data.substring(0,e.data.indexOf(":")) != 'mainForm.input<c:out value=""/>'){return;}. This means that any other format is rejected.

But we have all the information to build this format, because it was given to us in the querystring. We extract it with mes=window.location.search; and mes=mes.substring(mes.indexOf("item=")+5);. Now we must add the ":" and we're done.

And with this technique you now have a great way to add a Controlled Library to your OpenClinica-Studies.