Data propagation between JavaScript fields and dialogs
This page shows a full, minimal example of a custom JavaScript dialog field in Magnolia. It shows the two-way data propagation required for a functional field:
-
Dialog to field: Read the stored value when the dialog opens (initialization).
-
Field to dialog: Send your input back to the dialog so Magnolia can save it.
This example uses the raw postMessage API.
Standard dialogs require this API because they use the classic iframe rendering engine, not the newer Islands architecture used elsewhere in the UI.
|
Prerequisites
-
Familiarity with light modules.
-
You don’t need to do any Java development.
-
See these pages for background concepts:
Directory structure
Create this structure inside a light module:
my-light-module/
├── dialogs/
│ └── components/
│ └── my-js-dialog.yaml (1)
└── webresources/
└── my-field.html (2)
| 1 | The dialog definition registers the JavaScript field. |
| 2 | The HTML file provides the UI and logic. Magnolia renders it inside an iframe. |
Dialog definition (YAML)
Define a dialog with $type: javascriptField and point the fieldScript property to the HTML resource.
label: My JS Component
form:
properties:
title: (1)
$type: textField
label: Standard title
customMessage: (2)
$type: javascriptField
label: Custom JS input
fieldScript: /my-light-module/webresources/my-field.html
defaultValue: "Hello World"
| 1 | A standard Magnolia field for comparison. |
| 2 | Custom JavaScript field. |
Magnolia stores the value from the JavaScript field in JCR under the customMessage property, just like any other dialog property.
Field implementation (HTML + JavaScript)
Magnolia renders the HTML file inside an iframe.
Dialogs use the classic rendering engine, so you must use the native window.parent.postMessage API to communicate with Magnolia.
Capture the correlationId that Magnolia sends during initialization and include it in every outbound message.
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom JS Field</title>
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
input {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #dcdcdc;
border-radius: 2px;
}
</style>
</head>
<body>
<input type="text" id="jsInput" placeholder="Type here..." />
<script>
const inputElement = document.getElementById('jsInput');
let correlationId = null;
// ------------------------------------------------------------------
// 1. INBOUND: Initialize the field with data from Magnolia
// ------------------------------------------------------------------
window.addEventListener('message', function (event) {
const data = event.data;
// Magnolia sends an 'init' action when the dialog opens
if (data && data.action === 'init') {
// STORE THE ID: Required for the parent to recognize our messages
correlationId = data.correlationId;
// data.state.value -> value stored in JCR
// data.state.defaultValue -> value from dialog YAML
const initialValue = data.state.value || data.state.defaultValue || '';
inputElement.value = initialValue;
}
});
// ------------------------------------------------------------------
// 2. OUTBOUND: Propagate changes back to the dialog
// ------------------------------------------------------------------
inputElement.addEventListener('input', function (event) {
const newValue = event.target.value;
// Notify Magnolia that the field value has changed
// We must use the 'changeValue' action and include the correlationId
if (correlationId) {
window.parent.postMessage({
action: 'changeValue',
value: newValue,
correlationId: correlationId
}, '*');
}
});
</script>
</body>
</html>
Understand data propagation
Initialization (dialog to field)
When the dialog opens, Magnolia sends a postMessage to the iframe containing:
-
action: 'init' -
correlationId: A unique ID linking the iframe to the specific form field. -
state.value: The value currently stored in JCR, if any. -
state.defaultValue: The default value defined in the YAML definition.
The field script does the following:
-
Listens for the
messageevent. -
Detects the
initaction. -
Stores the
correlationId. This is crucial for step 2. -
Reads the value from
data.stateand updates the DOM.
Propagation (field to dialog)
When you type into the input field:
-
The
inputevent fires. -
The script calls
window.parent.postMessagewith thechangeValueaction. -
It includes the stored
correlationIdso Magnolia knows which field to update. -
This updates the dialog form state.
When you click Save changes, Magnolia persists the value to JCR.
If you don’t call changeValue, the dialog remains clean.
Even though you typed text into the HTML input, Magnolia doesn’t store anything.
|
Review the supported actions API
The dialog renderer listens for specific action types sent through postMessage.
All messages must include the correlationId you get during initialization.
| Action | Description | Payload structure |
|---|---|---|
|
Updates the value of the current field in the dialog form. |
|
|
Adjusts the height of the iframe (useful for dynamic content). |
|
|
Updates another field in the same dialog (cross-field dependency). |
|
|
Opens a different dialog (such as a chooser or sub-dialog). |
|
|
Proxies a REST request through Magnolia. |
|
Key takeaways
-
Legacy architecture: Dialogs run on the classic iframe infrastructure. You can’t use the
iframeClienthelper API here because it’s for UI apps. -
Correlation ID: You must manually capture and return the
correlationId. If this ID is missing, the dialog ignores your messages. -
Saving data: The dialog remains clean (unsaved) unless you explicitly send the
changeValueaction.