Step by Step Guide: Building a Data Entry Form That Matches the Paper One
When a section form cannot match the paper form, DHIS2 lets you write the form yourself. This guide covers the designer, the field identifier that makes a custom form work at all, layout in HTML, styling in CSS, running totals and warnings in JavaScript, and the maintenance you take on when you do it.
Section forms cover most needs and cost nothing to maintain. A custom form is HTML you own, which means you can build any layout, but you also own it forever.
| Form type | Good for | Cost |
|---|---|---|
| Default | Quick testing | None, but unreadable for long data sets |
| Section | Almost everything routine | None. Changes follow the data set |
| Custom | Paper forms that must be copied exactly, running totals, colour rules | You must edit the HTML every time the data set changes |
Every input in a custom form is tied to a data element and a category option combination by its id. Get the id right and DHIS2 saves the value; get it wrong and the field does nothing at all.
<input id="<dataElementUid>-<categoryOptionComboUid>-val">
<!-- for example -->
<input id="Uvn6LCg7dVU-Prlt0C1RF0s-val">A table is usually the right structure, because the paper form is usually a table.
<table class="malaria">
<tr>
<th>Malaria testing</th><th>Male</th><th>Female</th><th>Total</th>
</tr>
<tr>
<td>RDT tested</td>
<td><input id="Uvn6LCg7dVU-Prlt0C1RF0s-val"></td>
<td><input id="Uvn6LCg7dVU-bRTho9NV9dM-val"></td>
<td><input id="rdt-total" readonly></td>
</tr>
</table>| Element | Use it for |
|---|---|
| table, tr, th, td | The grid of the paper form |
| input | One field, with the id from Step 3 |
| input readonly | A total the script fills in |
| h3 and fieldset | Section headings |
| colspan and rowspan | Merged headings, as on paper |
Put the style in a single block at the top of the form. Keep it to layout and readability, and leave the DHIS2 colours for the states DHIS2 controls, such as green when a value is saved.
<style>
table.malaria { width: 100%; border-collapse: collapse; font-size: 14px; }
table.malaria th { background: #1f6fb2; color: #fff; padding: 8px; text-align: left; }
table.malaria td { border: 1px solid #d5dbe2; padding: 6px; }
table.malaria tr:nth-child(even) td { background: #f7f9fc; }
table.malaria input { width: 90%; padding: 4px; }
table.malaria input[readonly] { background: #eef2f6; font-weight: bold; }
.section-title { margin: 14px 0 6px; font-size: 16px; font-weight: 700; }
@media (max-width: 700px) { table.malaria { font-size: 12px; } }
</style>Scripts run inside the data entry page, so they can add up rows as they are typed and warn when a value cannot be right.
<script>
function id(de, coc) { return document.getElementById(de + "-" + coc + "-val"); }
function num(el) { return parseFloat(el && el.value) || 0; }
function updateTotals() {
var male = id("Uvn6LCg7dVU", "Prlt0C1RF0s");
var female = id("Uvn6LCg7dVU", "bRTho9NV9dM");
document.getElementById("rdt-total").value = num(male) + num(female);
var tested = num(male) + num(female);
var positive = num(id("hKZh1et5n7v", "Prlt0C1RF0s"));
var row = document.getElementById("rdt-positive-row");
row.style.background = positive > tested ? "#ffe0e0" : "";
}
document.addEventListener("keyup", updateTotals);
document.addEventListener("change", updateTotals);
</script>| What scripts are good at | What to leave to DHIS2 |
|---|---|
| Running totals as the clerk types | Saving values |
| Colouring a row that cannot be right | Validation rules, which are checked on the server |
| Hiding a section that does not apply | Sharing and access |
| Formatting and focus order | Minimum and maximum limits |
| Problem | Cause | Fix |
|---|---|---|
| Field does not save | The id is wrong | Delete it and use Insert data element |
| Totals never change | The script runs before the fields exist | Run it on keyup and change, as above |
| New data element missing from the form | A custom form does not follow the data set | Edit the HTML and add it |
| Form unusable on a phone | Fixed widths | Percentages and a media query |
| Values save but look wrong | Two inputs share one id | Each field has its own id |