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

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.

my-light-module/dialogs/components/my-js-dialog.yaml
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.
my-light-module/webresources/my-field.html
<!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:

  1. Listens for the message event.

  2. Detects the init action.

  3. Stores the correlationId. This is crucial for step 2.

  4. Reads the value from data.state and updates the DOM.

Propagation (field to dialog)

When you type into the input field:

  1. The input event fires.

  2. The script calls window.parent.postMessage with the changeValue action.

  3. It includes the stored correlationId so Magnolia knows which field to update.

  4. 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

changeValue

Updates the value of the current field in the dialog form.

{
  action: 'changeValue',
  correlationId: '...',
  value: 'new value'
}

changeHeight

Adjusts the height of the iframe (useful for dynamic content).

{
  action: 'changeHeight',
  correlationId: '...',
  value: 500
}

changeFormFieldValue

Updates another field in the same dialog (cross-field dependency).

{
  action: 'changeFormFieldValue',
  correlationId: '...',
  fieldName: 'otherField',
  value: 'val'
}

openDialog

Opens a different dialog (such as a chooser or sub-dialog).

{
  action: 'openDialog',
  correlationId: '...',
  dialogId: 'module:path/to/dialog'
}

callRestClient

Proxies a REST request through Magnolia.

{
  action: 'callRestClient',
  correlationId: '...',
  restClientName: '...',
  restClientMethodName: '...'
}

Key takeaways

  • Legacy architecture: Dialogs run on the classic iframe infrastructure. You can’t use the iframeClient helper 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 changeValue action.

Feedback

DX Core

×

Location

This widget lets you know where you are on the docs site.

You are currently perusing through the DX Core docs.

Main doc sections

DX Core Headless PaaS Legacy Cloud Incubator modules