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 toEncounterFormProcessor
. 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.
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:
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. Anobs
field, for example, will be rendered differently than anencounterLocation
field. Currently supportedtype
s 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 aCurrent 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 specifycalculateExpressions
. 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 theHistoricalEncounterDataService
. This service 'remembers' the last value entered in the input from the last encounter and offers the user the option to reuse that value.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.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": []}]}