HTML Form Entry Module HTML Reference


Reference for writing HTML for the Html_Form_Entry_Module

The htmlform tag

Your entire form must be wrapped in an htmlform tag

<htmlform>
     ...stuff goes here...
</htmlform>

Submit button

You need to put a submit tag at the bottom of your form, or else your users will be very disappointed in you.

<htmlform>
     ...stuff goes here...
     <submit/>
</htmlform>

Macros

You may define some macros at the top of the file (e.g. for things like colors)

<htmlform>
    <macros>
        lightgrey=#e0e0e0
        lightblue=#e0e0ff
        darkblue=#4444ff
    </macros>
    <div style="background-color: $lightblue">This is a pleasant light blue color</div>
</htmlform>

Encounter details

At present, a form creates one encounter, and you are required to fill in a datetime, location, and provider. These widgets don't display any label, just the relevant data entry widget

    <encounterDate/>                   <!-- an initially-blank date widget -->
    <encounterDate default="today"/>   <!-- a date widget pre-filled with today's date -->
    <encounterDate showTime="true"/>   <!-- a date and time widget -->
    <encounterDate showTime="true" default="now"/>   <!-- a date and time widget defaulting to the time on the server -->

    <encounterLocation/>                                    <!-- a list of all locations, sorted alphabetically by name -->
    <encounterLocation default="2"/>                        <!-- a list of all locations, sorted alphabetically by name, with location 2 selected by default -->
    <encounterLocation order="2,7,4"/>                      <!-- a list of the specified locations, by id -->
    <encounterLocation order="Rwinkwavu,Kirehe,Mulindi"/>   <!-- a list of the specified locations, by name -->

    <encounterProvider/>                   <!-- a list of all users -->
    <encounterProvider default="djaz"/>    <!-- a list of all users, with the user whose username is djaz selected -->
    <encounterProvider default="6"/>       <!-- a list of all users, with the user whose userId is 6 selected -->
    <encounterProvider role="Provider"/>   <!-- a list of those users with the Provider role -->

Observations

The obs tag generates an obs data entry widget. A few attributes may be specified for any type of obs:

  • conceptId="1234" The datatype of this concept drives the behavior of the widget. [REQUIRED]
  • labelText="The label that goes before the widget" [OPTIONAL]
  • labelNameTag="default" will delegate to Concept.getBestName(Context.getLocale()). Currently that's the only legal tag. [OPTIONAL]
  • showDate="true" means show a date widget for obs.datetime [OPTIONAL]
  • dateLabel="Date:" gives the text to display after the obs.value widget and before the obs.datetime widget. Specifying this implies showDate="true" [OPTIONAL]

Depending on the datatype of the concept you specify, you get different obs.value widgets, and you use different attributes to control them

Numeric

By default you'll get a small text box that auto-checks that entries are between absoluteMinimum and absoluteMaximum for the concept, and checks on precise also. You can also specify a constrained list of answers, as radio button or checkboxes. (Since 1.6.1) Example:

<!-- free text box (constrained to legal numbers) -->
<obs conceptId="5497" labelText="Most recent CD4:" dateLabel="Date of test:"/>

<!-- constrained to None, 1-6, and 7-8 -->
<obs conceptId="123" labelText="Education" answers="0,6,8" answerLabels="None,1-6,7-8" style="radio"/>
<obs conceptId="123" labelText="Education" answers="0,6,8" answerLabels="None,1-6,7-8" style="dropdown"/>

Boolean

style="checkbox"                 A checkbox that when checked creates a TRUE obs, and when unchecked does nothing
style="checkbox" value="false"   A checkbox that when checked creates a FALSE obs, and when unchecked does nothing
style="no_yes"                   Radio buttons for no and yes. You may click on a selected radio button to unselect it.
style="yes_no"                   Like above but reverse order
style="no_yes_dropdown"          Dropdown list with blank, no, and yes options.
style="yes_no_dropdown"          Like above but reverse order

<!-- Localization -->
<obs style="no_yes" noLabel="Non" yesLabel="Oui"/>

Text

  • "Normal" free-text operation
    • size="5" means a text field with the specified size
    • style="textarea" means a textarea
    • rows="10" number of rows in the textarea (implies style="textarea")
    • cols="80" number of columns in the textarea (implies style="textarea")
  • Constrained answers (since 1.6.1, see Numeric examples)
    • style="radio" or style="dropdown"
    • answers="comma,separated,legal,answers"
    • answerLabels="Display,For,Legal,Answers" (optional)
  • style="location" provides a drop-down list of Locations.

Date

You get a date picker widget. Note that dates are not allowed to be in the future, unless you specify allowFutureDates="true"!

Coded

  • style="radio" prints out all answers in option form
  • multiple="true" (Not implemented. Use multiple calls to <obs conceptId="1234" answerConceptId="1235" instead)
  • answerClasses="Finding,Diagnosis" returns a drop-down containing all concepts matching the passed ConceptClass names
Examples:
<obs conceptId="1234" answerConceptId="123" labelText="The question:" answerLabel="The answer"/>
    Renders as: The question: [ ]The answer
    If checked creates an obs with conceptId=1234 and valueCoded=123

<obs conceptId="1234"/>
    Renders as a dropdown of all possible answer specified for concept 1234 in the dictionary

<obs conceptId="1234" answerConceptIds="123,7,912" answerLabels="Malaria,Tuberculosis,Diabetes"/>
    Renders as a dropdown of the specified answers with the specified labels.

<obs conceptId="1296" answerClasses="Drug"/>
    Renders as a dropdown all Concepts with ConceptClass = "Drug", ordered by Concept Name

Obs Groups

You may use the obsgroup tag to create an obs group. You are required to specify a groupingConceptId. Nested obs groups are not currently supported.

<obsgroup groupingConceptId="1234">
    <obs conceptId="1234" answerConceptId="123" answerLabel="Other"/>
    <obs conceptId="987" labelText="specify:"/>
</obsgroup>

You can wrap an obsgroup tag anywhere in the html, generally speaking, as long as it is still a well-formed xml document.

This is okay:
   <table>
       <tr>
           <obsgroup groupingConceptId="1234">
               <td><obs conceptId="5089" labelText="Weight:"/></td>
               <td><obs conceptId="5090" labelText="Height:"/></td>
           </obsgroup>
       </tr>
   </table>

This will break!
   <obsgroup groupingConceptId="1234">
       <table>
           <tr>
               <td><obs conceptId="5089" labelText="Weight:"/></td>
               <td><obs conceptId="5090" labelText="Height:"/></td>
           </obsgroup>
       </tr>
   </table>

Program enrollments

You may have a form automatically enroll a patient in a program when the form is submitted. This only happens when the form is first entered--nothing happens when the form is edited or deleted.

<enrollInProgram programId="1"/>    enroll the patient in program 1 on the encounter's encounterDatetime

Specifics about this enrollment behavior:

  • If the
  • If the patient is already in the program on that date, nothing changes
  • If the patient is registered in the program after that date, then the patient's enrollment date will be moved back to the date of this encounter
    • if you don't want this behavior, please complain on the mailing list
  • Otherwise, the patient is enrolled as requested

Velocity expressions

The lookup tag allows you to evaluate velocity expressions:

<lookup expression="patient.personName"/> this will translate to the velocity expression $!{patient.personName}

<lookup expression="patient.getPatientIdentifier(5)"/> this will get the patient's first identifier with Identifier Type Id of 5

<lookup complexExpression="#foreach( $addr in $patient.addresses ) $!addr.cityVillage <br/> #end"/>

<lookup class="value" .../>   this will produce <span class="value">result of expression</span>

The variables you have access to in the velocity context are:

patient
the patient object you're entering the form for
locale
the authenticated user's locale
patientIdentifiers
Map<String, List<String>> of identifierType.name -> all identifiers of that type


Repeat Tag

Repeat tag - very useful tag for reusing code. Repeatedly renders code that is contained within template tags. Facilitates future code maintenance.

<repeat>
   <template>
      <code to be repeated/>
      <more code to be repeated/>
   </template>
   <render/>
   <render/>
</repeat>

Repeats can be nested inside of other tags. A repeat can contain 2 top level tags: template and render. thus the following code will not work:

As of 7/11/09, the > and < characters inside of a repeat tag cause an error.

<repeat>
  <table> <!-- this will cause an error -->
   <template>
      <code to be repeated/>
      <more code to be repeated/>
   </template>
   <render/>
   <render/>
  </table>
</repeat>

Examples

link HTMLforms

Linking to Pages

To make it seem like your html form has multiple pages, it would be best to put links to allow the user to jump down to the next page:

<a href="#page1">1</a>
Jump to Page: <b>1</b> | <a href="#page2">2</a> | <a href="#page3">3</a>
...
...
<hr/>
<a name="page2"></a>
Jump to Page: <a href="#page1">1</a> | <b>2</b> | <a href="#page3">3</a>
...
...
<hr/>
<a name="page3"></a>
Jump to Page: <a href="#page1">1</a> | <a href="#page2">2</a> | <b>3</b>
...

Making new forms

1. Always start with the actual paper form being used. It is important that clinicians already use this form and have "bought in" to it. If they don't really like it then they might decide to scrap it later and your work will be for nothing.

2. Print out the form. Also, if there is an InfoPath form try to get it.

3. If an InfoPath version exists, go through the paper copy and write down each concept used next to the question on the form for it. Keep in mind that the InfoPath version may contain data mapping errors. Note this on the paper form so you can come back to them later. For questions not replicated on the InfoPath form, or if you don't have the InfoPath version, go through the paper form and look up each concept in your concept dictionary. Write these down on your paper copy along with whatever data hiearchies you want (obs groups, coded questions, etc.)

4. Keep in mind that certain information may be best if left out of a htmlform and instead directly inputted to the EMR. These include many patient demographics and drug regimens.

5. Once your data scheme is on paper, you may begin the process of writing the form. Many forms employ numerous tables or other styles that are relatively simple to execute in html. A good html tutorial can be found here. Using html to style your form you should be able to closely replicate most paper forms. Images are not currently handled well and should be left out.

6. You may find it helpful to periodically preview your htmlform using that function in openMRS. Failure to do so can lead to troubles with debugging later as errors pile up. A good practice is to

7. Once the form has been implemented you should review the data schema to make sure that all concepts are mapping appropriately. Try the form out with testing data. If all looks well send to clinicians and/or share with others.



More will be added here as requested.