跳至内容

OpenClinica 用户手册/长列表

来自 Wikibooks,开放世界中的开放书籍

对于单选等响应类型,Excel 模板中的 RESPONSE_VALUES_OR_CALCULATIONS 字段包含用户可用的选项描述。此字段允许的最大字符数为 4000。虽然这通常足够,但在某些情况下,处理非常长的列表时,它就不够用了。本页提供了三种方法,说明如何规避此问题。第一种方法适用于 CRF 中的长列表是某个非重复组的一部分的情况。在此方法中,将一个非 OC 单选添加到 CRF 中,并用来填充一个只读的 OC 文本字段。第二种方法描述了如何处理重复组。它涉及找到动态创建的 ID、打开一个包含长列表的新窗口,并根据所选值更改 CRF 中一个只读文本的值。最后一种方法是对第二种方法的改进,它为单选添加了一个工具提示。

本页始终使用解剖学治疗化学 (ATC) 分类作为示例,该列表包含药物代码和描述。

方法 1:非重复组中的长列表

[编辑 | 编辑源代码]

在此方法中,一般思路如下:

  • 创建一个描述长列表的外部 XML 文件。
  • 创建一个包含 OC 文本字段和非 OC 单选的 CRF。
  • 在 CRF 中添加一些脚本,这些脚本可以:
    • 将文本字段设置为只读。
    • 读取 XML 文件并填充非 OC 单选。
    • 将所选值从单选复制到文本字段。

外部 XML

[编辑 | 编辑源代码]

外部 XML 文件应该描述您的列表。为了说明 XML 文件应该是什么样子,以下是一个示例:

<?xml version="1.0" encoding="utf-8" ?>
<ATCList>
	<ATCCode Code="A02BC01">
		<Description>A02BC01 - Omeprazole</Description>
	</ATCCode>
	<ATCCode Code="A02BC02">
		<Description>A02BC02 - Pantoprazole</Description>
	</ATCCode>
   ...
</ATCList>

示例文件名为“ATC_Codes.xml”。该文件必须存储在您的 OC 服务器上,例如在 includes 目录中,以便从您的 CRF 访问它。

工作原理

[编辑 | 编辑源代码]

XML 必须包含一个根元素,它只是列表的名称,<ATCList>;它不会在任何地方使用。接下来,必须定义元素。ATCCode 是元素的名称,它将在后面的 JavaScript 中用来查找元素。Code 是实际的代码,它基本上是 Excel 的 RESPONSE_OPTIONS_TEXT 条目之一。Description 是用户为此代码可见的描述,类似于 Excel 的 RESPONSE_VALUES_OR_CALCULATIONS。

CRF 需要两项:

  • 一个文本字段,在示例中称为 textOut。
  • 一个非 OC 单选,在示例中称为 myList。

textOut 只是一个普通的 OC 文本字段。为了能够在脚本中找到它,它必须用 span 标签包围,并指定一个标识符。因此,LEFT_ITEM_TEXT 变为:<span id=myOutput>textOut</span>

可以通过应用 HTML “select” 标签并通过 “option” 标签添加选项来创建非 OC 单选。

<select id="myList">
   <option val="None"/>None
</select>

这将创建一个标识符为“myList”的单选,并且当前它有一个名为“None”的选项。该下拉列表的一个不错的放置位置是 textOut 的右侧,可以通过将 HTML 代码粘贴到 RIGHT_ITEM_TEXT 中来完成。到目前为止,CRF 看起来像这样:

Method 1: initial CRF
方法 1:初始 CRF


剩下的工作就是添加一些脚本,这些脚本可以将 textOut 字段设置为只读、读取 xml 文件、填充单选并将单选的值复制到 textOut 字段。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script lang="Javascript">
   $.noConflict();
   jQuery(document).ready(function($) {
      var myField = $("#myList")
      var myOutputField = $("#myOutput").parent().parent().find("input");
      myOutputField.attr("readonly",true); 

      $.ajax({
         type: "GET",
         url: "includes/ATC_Codes.xml",
         dataType: "xml",
         success: parseXML
      });
      
      function parseXML(xml){
         $(xml).find("ATCCode").each(function(){
            myField.append($("<option />").val($(this).attr("Code")).text($(this).find("Description").text()));
         });
         myField.val(myOutputField.val());
      }

      myField.change(function(){
         myOutputField.val(myField.val());
         myOutputField.change();
      });
   });
</script>

将此粘贴到 RIGHT_ITEM_TEXT 的一个位置。必须更改四项以适应您的具体情况:

  • url: “includes/ATC_Codes.xml” 必须包含指向 xml 文件位置的链接。
  • 在 parseXML 函数中,“ATCCode”、“Code”和“Description”必须与您在 xml 文件中定义的标签匹配。

工作原理

[编辑 | 编辑源代码]
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script lang="Javascript">
   $.noConflict();

我们首先要做的是包含 jQuery 1.9.1。您也可以选择包含 OpenClinica 的 jQuery 版本。此外,我们通过调用 noConflict 来防止与 OpenClinica 的代码发生可能的冲突。

   var myField = $("#myList")
   var myOutputField = $("#myOutput").parent().parent().find("input");
   myOutputField.attr("readonly",true);

为了访问非 OC 单选和 OC 文本字段,我们使用前面定义的标识符定义了两个变量 myField 和 myOutputField。然后,我们将 myOutoutField 设置为只读,以防止用户手动输入值。

      myField.change(function(){
         myOutputField.val(myField.val());
         myOutputField.change();
      });

当 myField(单选列表)更改时,输出字段的值将设置为单选列表的值。然后,OC 会通过调用字段的 change 函数来通知字段已更改。这确保了在按下保存按钮时将数据保存到数据库中。

   $.ajax({
      type: "GET",
      url: "includes/ATC_Codes.xml",
      dataType: "xml",
      success: parseXML
   });

在此,使用 ajax 来读取 xml 文件。url 指向文件的位置。当文件成功读取后,将调用 parseXML 函数。

   function parseXML(xml){
      //find every ATCCode and Description
      $(xml).find("ATCCode").each(function(){
         myField.append($("<option />").val($(this).attr("Code")).text($(this).find("Description").text()));
      });
      myField.val(myOutputField.val());
   }

在我们的 XML 文件中,我们定义了“ATCCode”。parseXML 会尝试在 xml 文件中找到 ATCCode 的每个实例,并为每个实例将一个 option 标签追加到单选 myField。option 具有一个值,该值是 xml 文件中“Code”后面的值,以及一个描述性文本,该文本是 xml 文件中“Description”的值。最后,将单选的当前值设置为文本字段的当前值。这确保了在用户在之前保存数据后输入表单时,两者匹配。

CRF 现在看起来像这样:

Method 1: complete CRF
方法 1:完整 CRF


将 XML 文件上传到您的 OC 服务器并将您的 CRF 上传到 OC 后,您应该得到以下内容:

Method 1: CRF in OC
方法 1:OC 中的 CRF


方法 2:重复组中的长列表

[编辑 | 编辑源代码]

重复组需要不同方法的原因主要有两个:

  • 非 OC 单选不会出现在重复组中,因为它不是模板的一部分。因此,需要另一种方法来向用户提供长列表。
  • 处理标识符在重复组中更加复杂。在方法 1 中,通过将 span 标签应用于 OC 文本字段,我们得到了一个指向目标字段的静态链接。对于重复组,字段是动态生成的,因此 span 标签方法不再适用。


在第二种方法中,一般思路如下:

  • 创建一个描述长列表的外部 XML 文件。
  • 创建一个包含组中 OC 文本字段的 CRF。
  • 在 CRF 中添加一些脚本,这些脚本可以:
    • 将文本字段设置为只读。
    • 检查是否需要打开新窗口,如果需要,则打开窗口。
  • 创建一个包含长列表的外部 HTML 文件,并且:
    • 读取 XML 文件并填充非 OC 单选。
    • 将所选值复制回 OC 表单。

第一步,创建外部 XML 文件,在方法 1 中进行了描述,并且是相同的。

CRF 需要一项:

  • 一个文本字段,在我们的示例中称为 textOut。

然而,如果我们在组中添加一些额外的字段,它会变得更清晰。因此我们还添加

  • 一个文本字段,text1
  • 一个文本字段,text3

text1 是第一个项目,textOut 是第二个项目,text3 是重复组中的第三个项目。所有项目都在同一个组中,在本例中为 "MyGroup"。

脚本

[edit | edit source]

要打开包含单选的窗口,需要在 CRF 中添加一些脚本。一个粘贴此脚本的位置是 LEFT_ITEM_TEXT。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script lang="Javascript">
$.noConflict();
jQuery(document).ready(function($){
   function handlePopup(me){
      if(me.attr("ID")!=undefined){
         var myID = me.attr("ID");
         var gIndex = myID.indexOf(gName.toUpperCase());

         if(gIndex!=-1){
            var myParent = me.parent();
            var colNr = $(myParent).parent().children().index($(myParent)) + 1;

            if(colNr==aCol){
               atcID = myID;
               var handle = window.open("includes/ATC-Form.html");
            }
         }
      }
   }

   $("table.aka_form_table").on("click", ":input", function(){
      handlePopup($(this));
   });

   var aCol=2;
   var gName = "MyGroup";
   $("#srh").focus();
});
</script>

您需要针对您的具体情况更改三个项目

  • window.open("includes/ATC-Form.html") 必须包含指向 html 文件位置的链接(稍后解释)
  • aCol=2 是必须打开新窗口的项目的列号
  • gName="MyGroup" 必须包含包含必须打开新窗口的项目的重复组的名称(GROUP_LABEL)

CRF 现在看起来像这样:

Method 2: CRF
方法 2:CRF


工作原理

[edit | edit source]
$("table.aka_form_table").on("click", ":input", function(){
   handlePopup($(this));
});

由于用户可以动态地向重复组添加新行,我们无法再使用简单的 span 标签来获取目标输入项目的 id。因此,我们为每个输入项目触发 click 事件。为了确保该事件也针对新添加的行触发,应用了 "on" 函数。接下来,我们调用一个函数来处理我们的弹出窗口。

function handlePopup(me){
   if(me.attr("ID")!=undefined){
      var myID = me.attr("ID");
      var gIndex = myID.indexOf(gName.toUpperCase());

      if(gIndex!=-1){
         var myParent = me.parent();
         var colNr = $(myParent).parent().children().index($(myParent)) + 1;

对于刚刚触发事件的项目,我们首先检查它是否具有 ID。如果有,我们将 ID 存储在变量 myID 中。由于一个 CRF 中可能存在多个重复组,我们需要确保我们只为正确重复组中的正确项目打开一个新窗口。幸运的是,OC 根据 GROUP_LABEL 对重复组中的 ID 进行分类。因此,如果我们当前项目的标识符 (myID) 包含正确重复组的 GROUP_LABEL (gName),我们就知道该项目位于正确的组中。这是通过应用 indexOf 函数来完成的。如果返回值为 -1,则 myID 不包含 gName 字符串,并且 if 语句失败。此外,确定项目的列号。由于索引函数是基于 0 的,因此添加 1。


if(colNr==aCol){
   me.attr("readonly",true);
   atcID = myID;
   var handle = window.open("includes/ATC-Form.html");
}

如果列号也恰好是正确的,则该项目应该打开一个新窗口。但是,首先将字段设置为只读,以防止手动编辑,并将 myID 存储在全局 atcID 中。

   var aCol=2;
   var gName = "MyGroup";
   $("#srh").focus();

如前所述,aCol 和 gName 用于确保新窗口仅针对正确重复组和正确列中的项目打开。最后一条语句将页面的初始焦点设置为顶部的保存按钮。这可以防止用户在加载后手动编辑我们的 textOut 字段,而不是通过用户单击该字段来获得焦点。

外部 HTML 文件

[edit | edit source]

外部文件是一个简单的页面,包含动态填充的列表和一个选择按钮。

<html>
   <head>
      <title>Select ATC-code</title>
      <meta http-equiv="Expires" content="0"> <!-- disable caching -->
      <meta http-equiv="Content-type" content="text/html; charset=UTF-8"/>
      <meta http-equiv="X-UA-Compatible" content="IE=8" />

      <script src="jmesa/jquery-1.3.2.min.js"></script>
      <script type="text/javascript">
         jQuery(document).ready(function($) {
            $.ajax({
               type: "GET",
               url: "ATC_Codes.xml",
               dataType: "xml",
               success: parseXML
            });

         function parseXML(xml){
            $(xml).find("ATCCode").each(function(){
               field.append($("<option />").val($(this).attr("Code")).text($(this).find("Description").text()));
            });
         }

         $("#submit").click(function(){
            var atcElement = window.opener.document.getElementById(window.opener.atcID)
            if(atcElement.value!=$("#atc-list").val()){ 
               atcElement.value=$("#atc-list").val();
               atcElement.onchange();
            }
            close();
         });
         
         var field = $("#atc-list");
      });
      </script>
   </head>

   <body>
   <h1>Select an ATC-code</h1>
      <button type="button" id="submit">Select</button>
      <select id="atc-list"/></br>
   </body>
</html>

此表单必须存储在 OC 服务器上。在本例中,它存储在 includes 目录中,名为 "ATC-form.html"。需要更改几个项目以适合您的特定列表

  • 有关 ATC 代码的描述(例如,标题、标题等)
  • src="jmesa/jquery-1.3.2.min.js" 依赖于 html 表单的位置。如果它放置在服务器上的 includes 目录中,则无需更改它。
  • url: "ATC_Codes.xml" 必须包含指向您的 xml 文件位置的链接
  • 在 parseXML 函数中,“ATCCode”、“Code”和“Description”必须与您在 xml 文件中定义的标签匹配。
  • id="atc-list" 应该反映您的列表。如果您更改此项,请确保也更改所有 "#atc-list" 字符串

工作原理

[edit | edit source]

ajax 和 parseXML 方法在方法 1 中进行了解释。

   <body>
   <h1>Select an ATC-code</h1>
      <button type="button" id="submit">Select</button>
      <select id="atc-list"/></br>
   </body>

HTML 文档的主体包含一些文本、一个 ID 为 "submit" 的按钮和文本 "Select"。此外,它还包含一个 ID 为 "atc-list" 的空单选。

   $("#submit").click(function(){
      var atcElement = window.opener.document.getElementById(window.opener.atcID)
      if(atcElement.value!=$("#atc-list").val()){ 
         atcElement.value=$("#atc-list").val();
         atcElement.onchange();
      }
      close();
   });

当用户按下表单上的按钮时,将检索在 CRF 脚本中声明的全局变量 atcID,并使用此 ID 来查找实际字段 (atcElement)。如果字段的当前值不同于用户选择的 value,则更新该 value。调用 onchange() 以通知 OC,如果按下保存按钮,则必须保存该 value。最后,关闭窗口。

结果

[edit | edit source]

将 HTML 表单和 XML 文件上传到您的 OC 服务器并将您的 CRF 上传到 OC 后,您应该获得以下内容

Method 2: CRF in OC before selecting option
方法 2:选择选项之前的 OC 中的 CRF


Method 2: List in new window
方法 2:新窗口中的列表


Method 2: CRF in OC after selecting option
方法 2:选择选项后的 OC 中的 CRF


方法 3:使用工具提示的非重复组中的长列表

[edit | edit source]

在第二种方法中,我们打开一个新窗口来显示下拉列表。虽然新窗口为程序员提供了很大的自由度,但它的缺点是浏览器有时会阻止新窗口。另一种方法是应用 OC 中包含的工具提示。在第三种方法中,一般方法如下所示

  • 创建一个描述长列表的外部 XML 文件。
  • 创建一个包含组中 OC 文本字段的 CRF。
  • 在 CRF 中添加一些脚本,这些脚本可以:
    • 读取 XML 文件并将内容存储在变量中
    • 将文本字段设置为只读。
    • 检查是否需要显示工具提示,如果是,则显示包含单选的工具提示
    • 填充单选
    • 将选定的值从单选复制回表单

第一步,创建外部 XML 文件,在方法 1 中有描述,并且是相同的;第二步,创建 CRF,在第二种方法中描述,并且是相同的。

脚本

[edit | edit source]

要创建包含单选的工具提示,需要在 CRF 中添加一些脚本。一个粘贴此脚本的位置是 LEFT_ITEM_TEXT。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script lang="Javascript">
$.noConflict();
   jQuery(document).ready(function($) {
      var stringData;
 
      $.ajax({
         type: "GET",
         url: "includes/ATC_Codes.xml",
         dataType: "xml",
         success: parseXml
      });
 
      function parseXml(xml){
         stringData = "<option val='None'/>None";
         $(xml).find("ATCCode").each(function(){
            stringData=stringData+"<option val='"+$(this).attr("Code")+"'/>"+$(this).find("Description").text();
         });
      }

      startTip = function(atcID){
         var s="<select id='myList' onchange = 'changeValue()' atcID="+atcID+">"+stringData+"</select>";
         Tip(s, CLICKSTICKY, true, CLOSEBTN, true, FOLLOWMOUSE, false, WIDTH, 700);
      }
 
      changeValue = function(){
         var myOutputID = $("#myList").attr("atcID");
         var myOutput = document.getElementById(myOutputID);
         var selectedVal = $('option:selected', "#myList").attr("val");
         if(myOutput.value !== selectedVal){
            myOutput.value = selectedVal;
            myOutput.onchange();
         }
         UnTip();
      }
   
      $("table.aka_form_table").on("click", ":input", function(){ 
         var atcID = $(this).attr("id");
         var myParent = $(this).parent();
         var colNr = $(myParent).parent().children().index($(myParent)) + 1;
         var groupIndex = atcID.indexOf(groupName.toUpperCase());
         if(groupIndex!=-1){
            if(aCol==colNr){
               $(this).attr("readonly",true);       
               atcDescrID = $(this).parent().parent().children(":nth-child(3)").find(":input").attr("id")
               startTip(atcID);
            }
         }
      });
 
      var aCol=2;
      var groupName = "MyGroup";
      $("#srh").focus();
   });
</script>

您需要针对您的具体情况更改四个项目

  • url: “includes/ATC_Codes.xml” 必须包含指向 xml 文件位置的链接。
  • aCol=2 是必须打开新窗口的项目的列号
  • groupName="MyGroup" 必须包含包含必须显示弹出窗口的项目的重复组的名称(GROUP_LABEL)
  • WIDTH,700;此处 700 应该是您希望工具提示具有的宽度

工作原理

[edit | edit source]
$.ajax({
   type: "GET",
   url: "includes/ATC_Codes.xml",
   dataType: "xml",
   success: parseXml
});

我们再次使用 ajax 来解析我们的 XML 文件。

 
function parseXml(xml){
   stringData = "<option val='None'/>None";
   $(xml).find("ATCCode").each(function(){
      stringData=stringData+"<option val='"+$(this).attr("Code")+"'/>"+$(this).find("Description").text();
   });
}

但是这次,我们创建了一个名为 stringData 的字符串,其中包含所有选项和描述。

$("table.aka_form_table").on("click", ":input", function(){ 
  var atcID = $(this).attr("id");
  var myParent = $(this).parent();
  var colNr = $(myParent).parent().children().index($(myParent)) + 1;
  var groupIndex = atcID.indexOf(groupName.toUpperCase());
  if(groupIndex!=-1){
     if(aCol==colNr){
        $(this).attr("readonly",true);
        atcDescrID = $(this).parent().parent().children(":nth-child(3)").find(":input").attr("id")
        startTip(atcID);
     }
  }
});

其中大部分已经在方法 2 中解释过。但是,包含标识符的 atcID 现在存储在本地。此外,我们使用此标识符调用 startTip。

startTip = function(atcID){
   var s="<select id='myList' onchange = 'changeValue()' atcID="+atcID+">"+stringData+"</select>";
   Tip(s, CLICKSTICKY, true, CLOSEBTN, true, FOLLOWMOUSE, false, WIDTH, 700);
}

startTip 函数是实际启动工具提示的函数。首先,我们创建一个字符串 "s",它定义了工具提示中使用的下拉菜单。此 select 具有标识符 "myList",稍后用于检索选定的 value。接下来,atcID 与列表一起存储,这对于能够稍后查找单击的字段的标识符是必要的。然后,我们添加 stringData,它是我们在 parseXml 函数中生成的包含所有选项的完整字符串。其余的是工具提示选项 (http://www.walterzorn.de/en/tooltip/tooltip_e.htm)。第一个固定工具提示,第二个添加关闭按钮,第三个阻止工具提示跟随鼠标,最后一个设置宽度。

changeValue = function(){
   var myOutputID = $("#myList").attr("atcID");
   var myOutput = document.getElementById(myOutputID);
   var selectedVal = $('option:selected', "#myList").attr("val");

最后一个函数是 changeValue 函数,它在单选的 onchange 事件触发时被调用。要查找应写入输出的字段,请从列表中检索 atcID 属性。接下来,通过使用标识符调用 getElementByID 来检索实际元素。我们通过使用所选选项的 "val" 属性来检索选定的 value。

   if(myOutput.value !== selectedVal){
      myOutput.value = selectedVal;
      myOutput.onchange();
   }
   UnTip();

要检查该字段是否实际上需要更改,请将该字段的当前值与选定的值进行比较。如果它们不同,则将该字段更新为新的值,并调用 onchange 函数。最后,关闭工具提示。

结果

[edit | edit source]

将 XML 文件上传到您的 OC 服务器并将您的 CRF 上传到 OC 后,您应该获得以下内容

Method 3: CRF in OC before selecting option
方法 3:在选择选项之前,OC 中的 CRF


Method 3: CRF in OC showing tooltip
方法 3:在 OC 中显示工具提示的 CRF


Method 3: CRF in OC after selecting option
方法 3:在选择选项之后,OC 中的 CRF


许可信息

[编辑 | 编辑源代码]

本页面上的编程代码版权所有 2012 - 2014 VU University Medical Center / Center for Translational Molecular Medicine,可以根据 Apache 2.0 许可证重新使用。 除非适用法律要求或以书面形式同意,否则根据许可证分发的软件按“现状”提供,不附带任何明示或暗示的担保或条件。 有关管理许可证的权限和限制的具体语言,请参阅许可证。
您可以在 http://www.apache.org/licenses/LICENSE-2.0 获取许可证副本。

华夏公益教科书