Atlas Forms
Two-Way Binding
By default, every binding in Atlas Forms is two-way: the control reads from the source path on mount, and writes back to the form's value map whenever the user changes the value. The write is not to the original source object — it is to the engine's internal value map, which your submit handler then persists.
Read Phase (On Mount)
// 1. initialValues is the $json source:
const initialValues = {
firstName: 'Jane',
address: { city: 'London' }
};
// 2. Engine initialises value map from binding paths:
// Control 'first-name' has binding { source: '$json', path: 'firstName' }
// → engine.getValue('first-name') === 'Jane'
//
// Control 'city' has binding { source: '$json', path: 'address.city' }
// → engine.getValue('city') === 'London'
Write Phase (On User Change)
// 3. User types 'Manchester' in the city field
// ControlRenderer calls:
setFieldValue('city', 'Manchester')
// Which calls:
engine.setValue('city', 'Manchester')
// The engine fires the 'change' event with fieldId='city', value='Manchester'
// FormStateProvider re-renders; useAtlasForm() returns updated values:
// values['city'] === 'Manchester'
// The original initialValues object is NOT mutated:
// initialValues.address.city === 'London' ← still unchanged
Submit Phase
// 4. User clicks Submit
// submitForm() runs validation, then calls onSubmit with current values:
const handleSubmit = async (values: Record<string, any>) => {
// values is the engine's value map — NOT the original initialValues
// values['first-name'] === 'Jane'
// values['city'] === 'Manchester' ← updated value
await api.put('/applicants/42', {
firstName: values['first-name'],
address: {
city: values['city'],
}
});
};
One-Way Binding (readOnly)
Setting readOnly: true makes the binding one-way — read on mount, but user changes do not update the value map. Use this for display-only fields that should not be submitted:
// readOnly binding — shows the value but prevents user writes
{
"id": "applicant-id",
"type": "label",
"binding": {
"source": "$json",
"path": "id",
"readOnly": true
}
}
// engine.getValue('applicant-id') is set from initialValues.id on mount
// If the user somehow triggers a change event (e.g., via programmatic setValue),
// readOnly does NOT prevent the write — it only disables write from user interaction
Two-Way Binding Data Flow Summary
| Phase | Direction | What Happens |
|---|---|---|
| Mount | Source → Engine | Engine reads path from source; sets initial value in value map |
| User change | Engine (internal) | Engine updates value map; fires change event; React re-renders |
| Submit | Engine → Handler | onSubmit(values) called with full value map; handler persists |
The Source Object Is Never Mutated
Atlas Forms never mutates the
initialValues object you pass in. It reads from it once on mount, then maintains its own internal copy. This ensures your application state outside the form is not affected by form edits — until you explicitly call onSubmit and persist the new values yourself.