Core Concepts

AMPATH Forms provides a robust structure for working with forms and data. This guide will walk you through its underlying core concepts.

Form

A form is a collection of form fields. Forms are described using schemas written in JSON. The most basic form schema looks as follows:

{
"name": "",
"pages": [],
"processor": "EncounterFormProcessor",
"uuid": "xxxx",
"referencedForms": []
}

Form schemas get constructed using the Schema Editor. When you launch the Schema Editor, you get presented with a default form scaffold that resembles the example schema shown above.

Form properties

The default schema comprises of the following properties:

  • name: The name of the form.
ℹ️

Presently, form names must contain the word POC to be considered valid. For example, Test POC form is a valid form name while Yet Another Test form is not.

  • uuid: The unique form UUID. The form UUID is autogenerated by the Form Builder when you create and save a form successfully.

  • referencedForms: The collection of components that the form references. Components are reusable forms that encapsulate related logic. You can build a form by adding components to it using the Reference Forms feature of the Form Builder. Read more about referencing forms in the Referencing forms guide.

  • pages: A collection of pages that make up a form. You can add pages to a form by adding page definitions to the pages array. Pages consist of sections. Read more about pages in the Page guide.

  • processor: Defaults to EncounterFormProcessor. The form processor converts an OpenMRS REST form response into a format that the form renderer can process and display. Conversely, the form processor also converts the submitted form payload into a format that the REST backend can understand.

Components

There may be situations where you might want to separate commonly-used form logic into disparate reusable bits. In such cases, you might want to structure that logic as a component form. Component forms, or components for short, can therefore be thought of as reusable forms that carry domain-specific information. Imagine a situation where you're creating a bunch of Forms for use in a Point-of-Care setting. You might find that multiple forms might need to have sections for collecting Pre-Clinic Review information. This Pre-Clinic Review information could include details such as:

  • A patient's current HIV status
  • Whether a visit was scheduled or not
  • Reasons for a visit
  • The current visit type
  • A patient's insurance information

Now imagine having to define all of these sections and their accompanying questions in each of your forms. Components are the perfect tool for such situations. We'd need to create a Pre-Clinic Review component with all the relevant sections and questions. We'd then only need to reference this component in our forms and pick only the sections and questions we want.

As mentioned before, components and forms are functionally similar. The only difference between them is that you must use the prefix component in the form name when defining a component. You can view a list of all the components available in the system by toggling the Forms List view from POC Forms to Component Forms. Read more about components in the Referencing forms guide.

Page

Each page in your form schema gets rendered in a separate tab in the form viewer, so a page can be thought of as a way to encapsulate related form logic. Below is an example of a page definition for a page capturing information related to an encounter. This page definition consists of a page label, Encounter Details and a sections array. The page has one section labelled Encounter Info. That section, in turn, has two questions labelled Provider and Facility Name, respectively.

{
"pages": [
{
"label": "Encounter Details",
"sections": [
{
"label": "Encounter Info",
"isExpanded": "true",
"questions": [
{
"type": "encounterProvider",
"label": "Provider",
"id": "provider",
"required": "true",
"default": "",
"questionOptions": {
"rendering": "ui-select-extended"
}
},
{
"type": "encounterLocation",
"label": "Facility name (site/satellite clinic required)",
"id": "location",
"required": "true",
"questionOptions": {
"rendering": "ui-select-extended"
}
}
]
}
]
}
]
}

Here's how this page definition gets rendered in the Form Viewer.

Sample page

In practice, your form will likely have more than one page. As mentioned before, each page gets rendered as a tab in the form viewer. You can cycle through the pages by clicking the forward and back buttons at the bottom of the page. You can also click on a specific tab header to open that page. Here's how a form with multiple pages would look like:

Page with sections

Section

A section is an element of a form schema that groups together related questions. Sections will be rendered in "collapsed mode" by default. Set isExpanded to true in the section definition if you want section rendered in "expanded mode".

Below is an example of an Encounter Details page with a section labelled Encounter Info.

{
"pages": [
{
"label": "Encounter Details",
"sections": [
{
"label": "Encounter Info",
"isExpanded": "true",
"questions": [
{
"label": "Visit date",
"type": "encounterDatetime",
"required": "true",
"default": "",
"id": "encDate",
"questionOptions": {
"rendering": "date"
},
"validators": [
{
"type": "date"
}
]
},
{
"type": "encounterProvider",
"label": "Provider",
"id": "provider",
"required": "true",
"default": "",
"questionOptions": {
"rendering": "ui-select-extended"
}
},
{
"type": "encounterLocation",
"label": "Facility name",
"id": "location",
"required": "true",
"questionOptions": {
"rendering": "ui-select-extended"
}
}
]
}
]
}
]
}

Sections can also be constructed by referencing other sections from component forms imported in using the Reference forms feature.

{
// reference forms
"referencedForms": [
{
"formName": "component_lab-orders",
"alias": "lo",
"ref": {
"uuid": "ba985719-b085-419c-a0c7-3f1c3e61dd3e",
"display": "Lab Orders Component"
}
},
{
"formName": "component_art",
"alias": "art",
"ref": {
"uuid": "bd4ff44f-8007-49b0-b468-669fe0125093",
"display": "Anti-Retroviral Therapy Component"
}
},
]
// and then use the references to build up sections
{
"pages": [
{
"label": "Plan",
"sections": [
{
"reference": {
"form": "lo",
"page": "Test orders",
"section": "Test Orders"
}
}
]
},
{
"label": "Medication Plan",
"sections": [
{
"reference": {
"form": "art",
"page": "ART ",
"section": "ART Plan",
"excludeQuestions": [
"artStartedPed"
]
}
}
]
}
]
}
}

Question

A question is a field in the form. Below is an example of a question that seeks to establish the patient's Current HIV status:

{
"questions": [
{
"label": "Current HIV status",
"type": "obs",
"id": "hivStatus",
"questionOptions": {
"rendering": "select",
"concept": "9e4d6436-4040-46a3-a0ae-6dbc0acfe593",
"answers": [
{
"concept": "a896f3a6-1350-11df-a1f1-0026b9348838",
"label": "HIV positive"
},
{
"concept": "a896d2cc-1350-11df-a1f1-0026b9348838",
"label": "HIV negative"
},
{
"concept": "a899b50a-1350-11df-a1f1-0026b9348838",
"label": "Unknown"
}
]
},
"validators": []
}
]
}

Question properties

Here's a reference of the various properties you can specify in a question definition:

  • label: The actual content of the question. This label is what gets rendered as the question label.

  • id: An ID unique to that question. Used when validating the field input.

  • type: Provides information that the processor uses to render a field. An obs field, for example, will be rendered differently than an encounterLocation field. Currently supported types include: obs, obsGroup, testOrder, control, complex-obs, encounterDatetime, encounterLocation, encounterProvider, personAttribute.

  • questionOptions: An object containing the following properties:

    • rendering: The field type of the question. The most commonly used field types are text (for text inputs), select (for select fields) and date (for date fields). See the Field types reference for a full list of rendering types.

    • concept: The concept UUID of the backing concept for this field. You can get this concept UUID from searching in the Concept Dictionary.

    • answers: An array of answers scoped to a question. An answer definition consists of a concept UUID and label pair. Below is an example of answers to a Current HIV status question:

      {
      "answers": [
      {
      "concept": "a896f3a6-1350-11df-a1f1-0026b9348838",
      "label": "HIV positive"
      },
      {
      "concept": "a896d2cc-1350-11df-a1f1-0026b9348838",
      "label": "HIV negative"
      },
      {
      "concept": "a899b50a-1350-11df-a1f1-0026b9348838",
      "label": "Unknown"
      }
      ]
      }
    • calculate: An object where you can specify calculateExpressions. These are predefined expression helpers that take inputs and return numeric values. Read more about calculateExpressions in the Expression helper functions reference.

      {
      "label": "BMI (Kg/M2):",
      "questionInfo": "",
      "id": "bmi",
      "questionOptions": {
      "rendering": "number",
      "concept": "a89c60c0-1350-11df-a1f1-0026b9348838",
      "max": "100",
      "min": "0",
      "calculate": {
      "calculateExpression": "calcBMI(height,weight)"
      }
      },
      "type": "obs",
      "validators": []
      }
  • required: If set to true, that form field is considered a required field. Defaults to false.

  • validators: An array in which you provide validation logic for the specific question. Read more about validation in the Validating form fields reference.

  • historicalExpression: This allows you to hook your input up to the HistoricalEncounterDataService. This service 'remembers' the last value entered in the input from the last encounter and offers the user the option to reuse that value.

    Historical expressions

  • hide: You can use this to define logic for hiding a question based on certain conditions. To do so, you provide a JavaScript expression that evaluates to a boolean value.

    {
    "hide": {
    "hideWhenExpression": "onArt!== 'a899b35c-1350-11df-a1f1-0026b9348838'"
    }
    // This logic hides the question with the `onArt` id if the value of its
    // concept does not match the supplied value
    }
  • questionInfo: You can specify helper text for the question here. When specified, a question mark icon gets displayed to the right of the question label. When you hover over it, the information you entered gets displayed as a tooltip.

    Question info tooltip

    The code for this is as follows:

    {
    "label": "Person Collecting Medication",
    "questions": [
    {
    "label": "Visited by:",
    "id": "visitBy",
    "questionInfo": "Record the person that came for the clinic today, whether it is the patient or the treatment supporter.",
    "questionOptions": {
    "rendering": "select",
    "concept": "a89cd410-1350-11df-a1f1-0026b9348838",
    "answers": [
    {
    "concept": "a898c6f4-1350-11df-a1f1-0026b9348838",
    "label": "Self"
    },
    {
    "concept": "01b957da-23bb-4862-819d-036364fe3faf",
    "label": "Treatment supporter"
    }
    ]
    },
    "type": "obs",
    "validators": []
    }
    ]
    }