Atlas Forms
Binding Transforms
Transforms are named functions that convert a value between the format stored in the data source and the format displayed in the control. A read transform formats the stored value for display; a write transform parses the displayed value back to the stored format.
When to Use Transforms
| Scenario | read transform | write transform |
|---|---|---|
| Date stored as ISO 8601, displayed as locale string | ISO → locale date | Locale date → ISO |
| Amount stored in cents, displayed in pounds | ÷ 100 | × 100 |
| Boolean stored as 0/1 integer, displayed as Yes/No | 0/1 → false/true | false/true → 0/1 |
| CSV string stored, displayed as array of tags | split(',') | join(',') |
Registering a Transform
import { registerBindingTransform } from '@atlas-forms/form-engine-js';
// Register once at application startup
registerBindingTransform('isoToLocaleDate', (value: string): string => {
if (!value) return '';
const date = new Date(value);
return date.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
// e.g. '15 Jan 2026'
});
registerBindingTransform('localeDateToIso', (value: string): string => {
if (!value) return '';
// Parse '15 Jan 2026' → '2026-01-15'
const date = new Date(value);
return date.toISOString().split('T')[0];
});
registerBindingTransform('centsToGbp', (value: number): number => {
return typeof value === 'number' ? value / 100 : 0;
});
registerBindingTransform('gbpToCents', (value: number): number => {
return typeof value === 'number' ? Math.round(value * 100) : 0;
});
Using Transforms in a Schema
// Date field: stored as ISO, displayed as locale date
{
"id": "contract-start",
"type": "date-picker",
"label": "Contract Start Date",
"binding": {
"source": "$json",
"path": "contract.startDate",
"transform": {
"read": "isoToLocaleDate",
"write": "localeDateToIso"
}
}
}
// Amount field: stored in cents, displayed in pounds
{
"id": "invoice-amount",
"type": "number",
"label": "Invoice Amount (£)",
"binding": {
"source": "$json",
"path": "invoice.amountPence",
"transform": {
"read": "centsToGbp",
"write": "gbpToCents"
}
}
}
One-Directional Transforms
You can apply only a read transform (display formatting without write-back conversion) or only a write transform:
// Read-only display transform: show a code as a human-readable label
{
"binding": {
"source": "$json",
"path": "statusCode",
"transform": {
"read": "statusCodeToLabel" // write omitted — raw code is saved on submit
}
}
}
Transform Function Signature
type BindingTransformFn = (value: any) => any;
// Transforms must be pure functions — no side effects
// They may receive undefined/null and should handle gracefully
registerBindingTransform('myTransform', (value) => {
if (value == null) return ''; // Safe null handling
return String(value).toUpperCase();
});
Register Transforms Early
Transforms are looked up by name at mount time. If a schema references a transform name that has not been registered, the engine logs a warning and skips the transform (the raw value is used). Always register all transforms in your application bootstrap before any forms are rendered.