Technical Implementation of RFE forms
RFE Forms is a lightweight form engine written in React and powered by Formik and Carbon UX. It uses FHIR along with OpenMRS' Web Services to communicate with the associated OpenMRS instance.
Technically, RFE Forms is a React component that requires the following properties(props):
Mapping Controls
Fields are mapped to their controls via a registry system. This is highly extensible and easy to override the default controls.
{
"label":"Which of the following prevention services was the client referred to?",
"type":"obs",
"questionOptions":{
"rendering":"checkbox",
"concept":"5f394708-ca7d-4558-8d23-a73de181b02d",
"answers": [...]
},
"id":"referredToPreventionServices"
}
The field above has the rendering of type checkbox, the engine will simply check the registry for this kind of control and instantiates an instance of this control.
Submission Handlers
These handle submission at the field level. The engine doesn’t know how fields aggregate or compose primitive values into objects that the backend expects ie. an Observation. The form engine by default defines a set of base handlers eg. ObsSubmissionHandler
, EncounterLocationSubmissionHandler
etc.
These are also mapped using the registry system based on the type
property of a form field. On form submission, the engine simply collects all field submissions and aggregates them into some object that OpenMRS understands.
Extending the Registry
The engine exposes an API to work with the registry.
-
addHandler(handler: HandlerRegistryItem)
Adds submission handler to the handlers registryimport { SomeCustomHandler } from "./handlers"; import { addHandler } from "openmrs-ohri-form-engine-lib"; // Add to registry addHandler({ id: "customHandler", component: SomeCustomHandler, type: "someCustomType", });
-
addvalidator(validator: ValidatorRegistryItem)
Adds validator to the validators registry
import { SomeCustomValidator } from "./validators"; import { addvalidator } from "openmrs-ohri-form-engine-lib"; // Add to registry addvalidator({ id: "customValidator", component: SomeCustomValidator, type: "customValidatorType", });
-
addFieldComponent(control: ControlRegistryItem)
Adds custom control to the field controls registry
import { BootstrapDropdown } from "./controls"; import { addFieldComponent } from "openmrs-ohri-form-engine-lib"; // Add to registry addFieldComponent({ id: "bootstrapDropdown", component: BootstrapDropdown, type: "bootstrapDropdownType", });
Post Submission Actions
The form-engine supports post-submission actions. A post action is a JS class or function that implements this interface (opens in a new tab). This functional interface requires implementing the applyAction(formSession, config)
function which has the form session context plus an adhoc config. The config makes the actions more generic and configurable hence the ability of reusing the same action for different use-cases.
How do we register an action?
The engine utilises the registerPostSubmissionAction
function to register a post submission action.
Example:
HERE (opens in a new tab) is an example of an action that links mother to infant post delivery and here is how it's registered.
registerPostSubmissionAction({
name: 'MotherToChildLinkageSubmissionAction',
load: () => import('./post-submission-actions/mother-child-linkage-action'),
});
Take note that the registration happens in the app's index within the seeder func startupApp (opens in a new tab).
After the action is defined and registered, we simply have to reference the action within the target forms. This is the recommended and new way of doing things.
"availableIntents":[
{
"intent":"*",
"display":"Labour & Delivery Form"
}
],
"processor":"EncounterFormProcessor",
"uuid":"1e5614d6-5306-11e6-beb8-9e71128cae77",
"referencedForms":[],
"encounterType":"6dc5308d-27c9-4d49-b16f-2c5e3c759757",
"postSubmissionActions": [{"actionId": "MotherToChildLinkageSubmissionAction", "config": { "targetQueue": "Pre-Counselling", "isOptional": true }}],
"allowUnspecifiedAll":true,
"formOptions": {
"usePreviousValueDisabled": "true"
}
NOTE: We can also use the old way of linking post actions we didn't support configuring the actions then, see below. (The form engine still supports the old old way for linking actions)
"availableIntents": [
{
"intent": "*",
"display": "Labour & Delivery Form"
}
],
"processor": "EncounterFormProcessor",
"uuid": "1e5614d6-5306-11e6-beb8-9e71128cae77",
"referencedForms": [],
"encounterType": "6dc5308d-27c9-4d49-b16f-2c5e3c759757",
"postSubmissionActions": [
"MotherToChildLinkageSubmissionAction",
"ArtSubmissionAction"
],
"allowUnspecifiedAll": true,
"formOptions": {
"usePreviousValueDisabled": "true"
}
Program Enrolment
The ProgramEnrollmentSubmissionAction
is a generic post submission action that automatically enrols a patient to a specified program upon successful submission of an enncounter. This is passed through the form JSON and the action can either be enabled or disabled based on the expression passed as the enabled
flag. Below is an example of TB Program enrolment in the TB Case enrolment Form.
"postSubmissionActions": [
{
"actionId": "ProgramEnrollmentSubmissionAction",
"enabled":"tbProgramType === '160541AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'",
"config": {
"enrollmentDate": "tbRegDate",
"programUuid": "58005eb2-4560-4ada-b7bb-67a5cffa0a27",
"completionDate": "outcomeTBRx"
}
},
{
"actionId": "ProgramEnrollmentSubmissionAction",
"enabled":"tbProgramType === '160052AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'",
"config": {
"enrollmentDate": "tbRegDate",
"programUuid": "00f37871-0578-4ebc-af1d-e4b3ce75310d",
"completionDate": "outcomeTBRx"
}
}
]
We can add as many post submission actions as we want to one form and only those whose enabled
flag evaluates to true will be processed.